Что такое Python. ("точка точка") синтаксис нотации?


недавно я столкнулся с синтаксисом, который я никогда не видел раньше, когда я изучал python, ни в большинстве учебников,.. обозначения, это выглядит примерно так:

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

но мои вопросы:

  • как он может это сделать?
  • что это на самом деле означает с двумя точками?
  • как вы можете использовать его в более сложное высказывание (Если возможно)?

Это, вероятно, сэкономит мне много строк кода в будущем...:)

4 185

4 ответа:

ты float литерал без завершающего нуля, который вы затем получаете доступ к __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 Rooneys ответ), но также можно проверить правильность этих ответов.

позвольте мне повторить существующие ответы:.. - это не один элемент синтаксиса!

вы можете проверить исходный код "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 attribute") и __truediv__ - это имя метода. Так что это просто доступ к __truediv__ метод поплавка 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 (the 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

что это f = 1..__truediv__?

f - это связанный специальный метод на поплавке со значением единицы. В частности,

1.0 / x

в Python 3, вызывает:

(1.0).__truediv__(x)

доказательства:

class Float(float):
    def __truediv__(self, other):
        print('__truediv__ called')
        return super(Float, self).__truediv__(other)

и:

>>> one = Float(1)
>>> one/2
__truediv__ called
0.5

если мы это сделаем:

f = one.__truediv__

мы сохраняем имя, привязанное к этому связанному методу

>>> f(2)
__truediv__ called
0.5
>>> f(3)
__truediv__ called
0.3333333333333333

если бы мы делали этот точечный поиск в узком цикле, это могло бы немного сэкономить время.

разбор абстрактного синтаксического дерева (AST)

мы видим, что разбор AST для выражения говорит нам, что мы получаем