Недавно я столкнулся с синтаксисом, который я никогда раньше не видел, когда изучал питон и в большинстве руководств, нотацию ..
, он выглядит примерно так:
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
без конечного нуля, к которому вы затем обращаетесь к методу __truediv__
. Это не сам оператор; первая точка является частью значения поплавка, а вторая является оператором точек для доступа к свойствам и методам объектов.
Вы можете достичь одной и той же точки, выполнив следующие действия.
>>> 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), но также можно проверить правильность этих ответов.
Позвольте мне повторить существующие ответы: ..
не является одним синтаксическим элементом!
Вы можете проверить, как исходный код "tokenized" . Эти токены представляют собой интерпретацию кода:
>>> 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 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)
будет еще лучше!
Подход с использованием partial
также сохраняет модель данных python (подход 1..__truediv__
не работает!), что может быть продемонстрировано этим маленьким фрагмент кода:
>>> 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
Это связано с тем, что 1. / (1+2j)
не оценивается float.__truediv__
, но при complex.__rtruediv__
- operator.truediv
выполняется обратная операция, когда нормальная операция возвращает NotImplemented
, но у вас нет этих резервных копий, когда вы непосредственно на __truediv__
. Эта потеря "ожидаемого поведения" является основной причиной, по которой вы (обычно) не должны использовать магические методы напрямую.
Две точки вместе могут быть немного неудобными сначала:
f = 1..__truediv__ # or 1..__div__ for python 2
Но это то же самое, что и запись:
f = 1.0.__truediv__ # or 1.0.__div__ for python 2
Поскольку литералы float
могут быть записаны в трех формах:
normal_float = 1.0
short_float = 1. # == 1.0
prefixed_float = .1 # == 0.1
1.__truediv__
нет?
.
кажется, анализируется как часть числа, а затем .
для метода метод доступа отсутствует.
Что такое
f = 1..__truediv__
?
Мы видим, что анализ АСТ для выражения говорит нам, что мы получаем атрибут __truediv__
для числа с плавающей запятой, 1.0
:
>>> import ast
>>> ast.dump(ast.parse('1..__truediv__').body[0])
"Expr(value=Attribute(value=Num(n=1.0), attr='__truediv__', ctx=Load()))"
Вы можете получить одну и ту же результирующую функцию:
f = float(1).__truediv__
или
f = (1.0).__truediv__
Мы также можем получить это путем вывода.
Позвольте создать его.
1 сам по себе является int
:
>>> 1
1
>>> type(1)
<type 'int'>
1 с периодом после его поплавка:
>>> 1.
1.0
>>> type(1.)
<type 'float'>
Следующая точка сама по себе будет SyntaxError, но она начнет пунктирный поиск в экземпляре float:
>>> 1..__truediv__
<method-wrapper '__truediv__' of float object at 0x0D1C7BF0>
Никто не упомянул об этом - теперь это привязанный метод " на float, 1.0
:
>>> f = 1..__truediv__
>>> f
<method-wrapper '__truediv__' of float object at 0x127F3CD8>
>>> f(2)
0.5
>>> f(3)
0.33333333333333331
Мы могли бы выполнить ту же функцию гораздо более читаемо:
>>> def divide_one_by(x):
... return 1.0/x
...
>>> divide_one_by(2)
0.5
>>> divide_one_by(3)
0.33333333333333331
Недостатком функции является то, что она требует другого фрейма стека Python, делая его несколько медленнее, чем связанный метод:
>>> 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]
Конечно, если вы можете просто использовать простые литералы, это еще быстрее:
>>> def f_3():
... for x in range(1, 11):
... 1.0/x
...
>>> timeit.repeat(f_3)
[2.1224895628296281, 2.1219930218637728, 2.1280188256941983]
(1).__truediv__
самом деле не то же самое, что1..__truediv__
, так как первый вызываетint.__truediv__
то время как последний действительноfloat.__truediv__
. Кроме того, вы также можете использовать1 .__truediv__
(с пробелом) `1//8
равно0
, а не0.125
, в любой версии Python.