web-dev-qa-db-ja.com

`1 ..__ truediv__`とは何ですか? 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

しかし私の質問は:

  • どうすればそれができますか?
  • それは実際には2つのドットとはどういう意味ですか?
  • 可能であれば、どのようにしてそれをより複雑なステートメントで使用できますか?

これはおそらく将来私に多くのコード行を節約するでしょう... :)

190
abccd

末尾にゼロが付いていないfloatリテラルがあります。これを使用しての__truediv__メソッドにアクセスします。それ自体は演算子ではありません。最初のドットはfloat値の一部であり、2番目のドットはオブジェクトのプロパティとメソッドにアクセスするためのドット演算子です。

次のようにしても同じポイントに到達できます。

>>> 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になります。

212
Paul Rooney

質問はすでに十分に回答されています(つまり @ Paul Rooney s回答)が、これらの回答の正当性を検証することも可能です。

..は単一の構文要素ではありません。

ソースコードが "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.は数値として解釈され、2番目の.はOP(演算子、この場合は "get attribute"演算子)、そして__truediv__はメソッド名です。つまり、これはfloat __truediv__1.0メソッドにアクセスするだけです。

生成されたバイトコードを表示するもう1つの方法は、 disassemble それです。これは実際にコードが実行されたときに実行される命令を示しています。

>>> 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__を直接操作するときこれらのフォールバックはありません。この「期待される振る舞い」の喪失が、(通常は)直接魔法の方法を使うべきではない主な理由です。

73
MSeifert

2つのドットを合わせると、最初は少しぎこちないかもしれません。

f = 1..__truediv__ # or 1..__div__ for python 2

しかし、それは書くことと同じです:

f = 1.0.__truediv__ # or 1.0.__div__ for python 2

floatリテラルは3つの形式で書くことができるからです。

normal_float = 1.0
short_float = 1.  # == 1.0
prefixed_float = .1  # == 0.1
40
sobolevn

f = 1..__truediv__とは何ですか?

fは、値が1のfloat型の特別なバインドメソッドです。具体的には、

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を解析すると、浮動小数点数__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>

他に誰もこれに言及していません - これはフロート上の"bound method"です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

パフォーマンス

divide_one_by関数のマイナス面は、それが別のPythonスタックフレームを必要とし、それがboundメソッドよりもいくぶん遅くなることです。

>>> 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]
11
Aaron Hall