「1 ..__ truediv__」とは何ですか?Pythonには..(「ドットドット」)表記構文がありますか?


190

私は最近、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つのドットは実際にはどういう意味ですか?
  • (可能な場合)より複雑なステートメントでどのように使用できますか?

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


14
注:前者がを呼び出し、後者がを呼び出す(1).__truediv__ため1..__truediv__、は実際にはと同じではありません。また、あなたはまた、使用することができます`(スペースで)int.__truediv__float.__truediv__1 .__truediv__
tobias_k

7
注意1//8され0ていない、0.125Pythonのいずれかのバージョンでは、。
mkrieger1

1
思い出すif (x <- 3) {...}
Dunno

7
ここで使用されているこの例はあります。
イーモンオリーブ

3
@KeithC高品質の回答とコメントは、サンプルコードが理解するための洞察が必要であることを示し、多くの人にとっては驚くべきことであり、より明確で、より一般的で、少なくとも同じくらい効率的な選択肢があります。私の主な不満は、読みやすさが重要であることです。人間とのコミュニケーション-それが最も必要とされる場所の賢さを保存します。
Peter Wood

回答:


212

あなたが持っているのはfloat、後続のゼロのないリテラルであり、それから__truediv__メソッドにアクセスします。それ自体は演算子ではありません。最初のドットは浮動小数点値の一部であり、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になります。


165
だから私たちが見つけたのは、少し簡潔にするために多くの明快さを犠牲にした開発者であり、ここにいます。
TemporalWolf

11
たぶん誰かがソースコードを5.5
インチ

10
@ThomasAyoub 5.25 "iirc ;-)
jjmontes

9
@TemporalWolf彼はこの最近のコードゴルフの提出でそれを発見したかもしれません。
ブライアンマッカッション

2
楽しい事実は、あなたはまた、JavaScriptでこれを行うことができます:1..toString()
デレク朕會功夫

74

質問はすでに十分に回答されています(つまり、@ 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.は数値として解釈され、2番目.はOP(演算子、この場合は「属性を取得」演算子)であり、__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)

さらに良いでしょう!

を使用したアプローチでは、Pythonのデータモデルpartialも維持されます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.truedivNotImplemented__truediv__


40

最初は2つのドットを組み合わせると少し扱いに​​くいかもしれません。

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__あるのですか?
アレックスホール

3
@AlexHallこちらをご覧ください.番号の一部として解析しているように見えるし、その後.メソッドアクセサのための不足しています。
tobias_k

7
しかし、それは扱いにくく、不明確な構文であるため、おそらく回避する必要があります。
DrMcCleod 2017

11

なにf = 1..__truediv__

f値が1のフロートにバインドされた特別なメソッドです。具体的には

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になりますが、フロートのインスタンスでドット付きルックアップを開始します。

>>> 1..__truediv__
<method-wrapper '__truediv__' of float object at 0x0D1C7BF0>

他の誰もこれについて言及していません -これは現在、フロートの「バインドされたメソッド」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スタックフレームが必要になるため、バインドされたメソッドよりも多少遅くなることです。

>>> 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]
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.