`1 ..__ truediv__`는 무엇입니까? 파이썬에는 .. ( "dot dot") 표기법 구문이 있습니까?
나는 최근에 파이썬을 배울 때 보지 못했던 구문이나 대부분의 자습서에서 ..
표기법을 보았습니다 .
f = 1..__truediv__ # or 1..__div__ for python 2
print(f(8)) # prints 0.125
나는 그것이 정확히 같음을 알았습니다 (물론 더 길다는 것을 제외하고).
f = lambda x: (1).__truediv__(x)
print(f(8)) # prints 0.125 or 1//8
그러나 내 질문은 :
- 어떻게 할 수 있습니까?
- 두 점에서 실제로 무엇을 의미합니까?
- 더 복잡한 문장에서 어떻게 사용할 수 있습니까 (가능한 경우)?
이것은 아마도 나에게 많은 코드 줄을 저장할 것입니다 ... :)
당신이 가진 것은 float
후행 0이없는 리터럴이며, 그런 다음 __truediv__
메소드에 액세스합니다 . 그 자체로는 연산자가 아닙니다. 첫 번째 점은 float 값의 일부이고 두 번째 점은 객체 속성 및 메서드에 액세스하는 점 연산자입니다.
다음을 수행하여 동일한 지점에 도달 할 수 있습니다.
>>> f = 1.
>>> f
1.0
>>> f.__floordiv__
<method-wrapper '__floordiv__' of float object at 0x7f9fb4dc1a20>
또 다른 예
>>> 1..__add__(2.)
3.0
여기에 1.0을 2.0으로 추가하면 분명히 3.0이됩니다.
질문은 이미 충분히 대답 되었지만 (예 : @Paul Rooney 의 답변) 이러한 답변의 정확성을 확인할 수도 있습니다.
기존 답변을 요약 해 보겠습니다 ..
. 단일 구문 요소가 아닙니다!
소스 코드가 어떻게 "토큰 화" 되었는지 확인할 수 있습니다 . 이 토큰은 코드가 해석되는 방식을 나타냅니다.
>>> from tokenize import tokenize
>>> from io import BytesIO
>>> s = "1..__truediv__"
>>> list(tokenize(BytesIO(s.encode('utf-8')).readline))
[...
TokenInfo(type=2 (NUMBER), string='1.', start=(1, 0), end=(1, 2), line='1..__truediv__'),
TokenInfo(type=53 (OP), string='.', start=(1, 2), end=(1, 3), line='1..__truediv__'),
TokenInfo(type=1 (NAME), string='__truediv__', start=(1, 3), end=(1, 14), line='1..__truediv__'),
...]
따라서 문자열 1.
은 숫자로 해석되고 두 번째 .
는 OP (이 경우 "get 속성"연산자)이고 __truediv__
메소드 이름입니다. 그래서 이것은 단지 __truediv__
float 메소드에 접근하고 1.0
있습니다.
생성 된 바이트 코드를 보는 또 다른 방법은이를 조립 하는 것입니다. 이것은 실제로 일부 코드가 실행될 때 수행되는 명령을 보여줍니다. dis
>>> import dis
>>> def f():
... return 1..__truediv__
>>> dis.dis(f)
4 0 LOAD_CONST 1 (1.0)
3 LOAD_ATTR 0 (__truediv__)
6 RETURN_VALUE
기본적으로 동일하게 말합니다. __truediv__
상수 의 속성 을 로드합니다 1.0
.
당신의 질문에 대해
그리고 더 복잡한 진술에서 어떻게 사용할 수 있습니까 (가능한 경우)?
비록 코드가 무엇을하고 있는지 확실하지 않기 때문에 그런 코드를 작성해서는 안된다. 더 복잡한 문장에서는 사용하지 마십시오. 나는 심지어 "간단한"문장에서 사용하지 말아야 할 것까지는 최소한 괄호를 사용하여 지침을 분리해야합니다.
f = (1.).__truediv__
이것은 명확하게 더 읽기 쉬울 것이지만 다음과 같은 내용이 있습니다.
from functools import partial
from operator import truediv
f = partial(truediv, 1.0)
더 좋을 것입니다!
The approach using partial
also preserves python's data model (the 1..__truediv__
approach does not!) which can be demonstrated by this little snippet:
>>> f1 = 1..__truediv__
>>> f2 = partial(truediv, 1.)
>>> f2(1+2j) # reciprocal of complex number - works
(0.2-0.4j)
>>> f2('a') # reciprocal of string should raise an exception
TypeError: unsupported operand type(s) for /: 'float' and 'str'
>>> f1(1+2j) # reciprocal of complex number - works but gives an unexpected result
NotImplemented
>>> f1('a') # reciprocal of string should raise an exception but it doesn't
NotImplemented
This is because 1. / (1+2j)
is not evaluated by float.__truediv__
but with complex.__rtruediv__
- operator.truediv
makes sure the reverse operation is called when the normal operation returns NotImplemented
but you don't have these fallbacks when you operate on __truediv__
directly. This loss of "expected behaviour" is the main reason why you (normally) shouldn't use magic methods directly.
Two dots together may be a little awkward at first:
f = 1..__truediv__ # or 1..__div__ for python 2
But it is the same as writing:
f = 1.0.__truediv__ # or 1.0.__div__ for python 2
Because float
literals can be written in three forms:
normal_float = 1.0
short_float = 1. # == 1.0
prefixed_float = .1 # == 0.1
What is
f = 1..__truediv__
?
f
is a bound special method on a float with a value of one. Specifically,
1.0 / x
in Python 3, invokes:
(1.0).__truediv__(x)
Evidence:
class Float(float):
def __truediv__(self, other):
print('__truediv__ called')
return super(Float, self).__truediv__(other)
and:
>>> one = Float(1)
>>> one/2
__truediv__ called
0.5
If we do:
f = one.__truediv__
We retain a name bound to that bound method
>>> f(2)
__truediv__ called
0.5
>>> f(3)
__truediv__ called
0.3333333333333333
If we were doing that dotted lookup in a tight loop, this could save a little time.
Parsing the Abstract Syntax Tree (AST)
We can see that parsing the AST for the expression tells us that we are getting the __truediv__
attribute on the floating point number, 1.0
:
>>> import ast
>>> ast.dump(ast.parse('1..__truediv__').body[0])
"Expr(value=Attribute(value=Num(n=1.0), attr='__truediv__', ctx=Load()))"
You could get the same resulting function from:
f = float(1).__truediv__
Or
f = (1.0).__truediv__
Deduction
We can also get there by deduction.
Let's build it up.
1 by itself is an int
:
>>> 1
1
>>> type(1)
<type 'int'>
1 with a period after it is a float:
>>> 1.
1.0
>>> type(1.)
<type 'float'>
The next dot by itself would be a SyntaxError, but it begins a dotted lookup on the instance of the float:
>>> 1..__truediv__
<method-wrapper '__truediv__' of float object at 0x0D1C7BF0>
No one else has mentioned this - This is now a "bound method" on the float, 1.0
:
>>> f = 1..__truediv__
>>> f
<method-wrapper '__truediv__' of float object at 0x127F3CD8>
>>> f(2)
0.5
>>> f(3)
0.33333333333333331
We could accomplish the same function much more readably:
>>> def divide_one_by(x):
... return 1.0/x
...
>>> divide_one_by(2)
0.5
>>> divide_one_by(3)
0.33333333333333331
Performance
The downside of the divide_one_by
function is that it requires another Python stack frame, making it somewhat slower than the bound method:
>>> def f_1():
... for x in range(1, 11):
... f(x)
...
>>> def f_2():
... for x in range(1, 11):
... divide_one_by(x)
...
>>> timeit.repeat(f_1)
[2.5495760687176485, 2.5585621018805469, 2.5411816588331888]
>>> timeit.repeat(f_2)
[3.479687248616699, 3.46196088706062, 3.473726342237768]
Of course, if you can just use plain literals, that's even faster:
>>> def f_3():
... for x in range(1, 11):
... 1.0/x
...
>>> timeit.repeat(f_3)
[2.1224895628296281, 2.1219930218637728, 2.1280188256941983]
'IT story' 카테고리의 다른 글
git-병합 할 때 특정 커밋 건너 뛰기 (0) | 2020.05.14 |
---|---|
JavaScript의 모든 잘못된 값 (0) | 2020.05.14 |
Vim에서는 한마디로 돌아가고 싶습니다. (0) | 2020.05.14 |
Django ImageField에 이미지를 프로그래밍 방식으로 저장 (0) | 2020.05.14 |
최소 및 최대 버전 범위로 패키지를 설치하는 방법은 무엇입니까? (0) | 2020.05.14 |