Apa `1..__truediv__` ? Tidak Python memiliki .. ("dot dot") notasi sintaks?

Saya baru-baru menemukan sebuah sintaks yang tidak pernah aku lihat sebelumnya ketika saya belajar python atau dalam kebanyakan tutorial, .. notasi, terlihat sesuatu seperti ini:

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

print(f(8)) # prints 0.125 

Saya pikir itu adalah persis sama seperti (kecuali's lagi, tentu saja):

f = lambda x: (1).__truediv__(x)
print(f(8)) # prints 0.125 or 1//8

Tapi pertanyaan saya adalah:

  • Bagaimana anda bisa melakukan itu?
  • Apa sebenarnya artinya dengan dua titik?
  • Bagaimana anda bisa menggunakannya dalam bentuk yang lebih kompleks pernyataan (jika mungkin)?

Ini mungkin akan menghemat saya banyak baris kode di masa depan...:)

Mengomentari pertanyaan (7)
Larutan

Apa yang anda miliki adalah float literal tanpa trailing zero, yang kemudian anda mengakses __truediv__ metode. It's tidak operator itu sendiri; pertama dot adalah bagian dari float nilai, dan yang kedua adalah operator dot untuk mengakses objek properti dan metode.

Anda dapat mencapai titik yang sama dengan melakukan hal berikut.

>>> f = 1.
>>> f
1.0
>>> f.__floordiv__

Contoh lain

>>> 1..__add__(2.)
3.0

Di sini kita tambahkan 1.0 ke 2.0, yang jelas hasil 3.0.

Komentar (7)

Pertanyaannya adalah sudah cukup menjawab (yaitu @Paul Rooneys jawaban) tapi itu's juga memungkinkan untuk memverifikasi kebenaran dari jawaban-jawaban ini.

Mari saya rekap yang ada jawaban: .. tidak satu sintaks elemen!

Anda dapat memeriksa bagaimana kode sumber "tokenized". Token ini mewakili berapa kode ditafsirkan:

>>> 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__'),
 ...]

Jadi string 1. diartikan sebagai jumlah, kedua . adalah OP (operator, dalam hal ini "mendapatkan atribut" operator) dan __truediv__ adalah nama metode. Jadi, ini adalah hanya mengakses __truediv__ metode float 1.0.

Cara lain untuk melihat bytecode yang dihasilkan adalah `dis'assemble hal. Ini benar-benar menunjukkan instruksi yang dilakukan ketika beberapa kode dieksekusi:

>>> 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

Yang pada dasarnya mengatakan hal yang sama. Itu banyak atribut __truediv__ konstan 1.0.


Mengenai pertanyaan anda

Dan bagaimana anda dapat menggunakannya pada yang lebih kompleks pernyataan (jika mungkin)?

Meskipun itu's mungkin anda harus menulis kode seperti itu, hanya karena itu's tidak jelas apa kode lakukan. Jadi silakan don't menggunakannya di lebih kompleks pernyataan. Aku bahkan akan pergi sejauh yang anda tidak't menggunakannya dalam "mudah" pernyataan, setidaknya anda harus menggunakan tanda kurung untuk memisahkan petunjuk:

f = (1.).__truediv__

ini akan pasti lebih mudah dibaca - tapi sesuatu di sepanjang baris:

from functools import partial
from operator import truediv
f = partial(truediv, 1.0)

akan lebih baik!

Dengan menggunakan pendekatan parsial juga menjaga python's model data (1..__truediv__ pendekatan tidak!) yang dapat ditunjukkan oleh ini sedikit cuplikan:

>>> 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

Hal ini karena 1. / (1+2j) adalah tidak dievaluasi oleh float.__truediv__, tapi dengan kompleks.__rtruediv__ - operator.truediv memastikan operasi sebaliknya disebut saat operasi normal kembali NotImplemented tapi anda don't memiliki fallbacks ketika anda beroperasi pada __truediv__ ini diatas. Ini kehilangan "perilaku yang diharapkan" adalah alasan utama mengapa anda (biasanya) tidak't menggunakan metode sihir ini diatas.

Komentar (0)

Dua titik bersama-sama mungkin sedikit canggung pada awalnya:

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

Tapi itu adalah sama seperti menulis:

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

Karena float literal dapat ditulis dalam tiga bentuk:

normal_float = 1.0
short_float = 1.  # == 1.0
prefixed_float = .1  # == 0.1
Komentar (3)

Apa f = 1..__truediv__?

f adalah terikat metode khusus di lampung dengan nilai satu. Secara khusus,

1.0 / x

di Python 3, memanggil:

(1.0).__truediv__(x)

Bukti:

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

dan:

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

Jika kita lakukan:

f = one.__truediv__

Kami mempertahankan nama terikat yang terikat metode

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

Jika kita melakukan itu dihiasi lookup di sebuah lingkaran yang ketat, hal ini bisa sedikit menghemat waktu.

Parsing Abstrak Pohon Sintaks (AST)

Kita dapat melihat bahwa parsing AST untuk ekspresi memberitahu kita bahwa kita mendapatkan __truediv__ atribut pada angka floating point, 1.0:

>>> import ast
>>> ast.dump(ast.parse('1..__truediv__').body[0])
"Expr(value=Attribute(value=Num(n=1.0), attr='__truediv__', ctx=Load()))"

Anda bisa mendapatkan hal yang sama sehingga fungsi dari:

f = float(1).__truediv__

Atau

f = (1.0).__truediv__

Deduksi

Kita juga bisa mendapatkan di sana dengan deduksi.

Let's membangunnya.

1 dengan sendirinya adalah sebuah int:

>>> 1
1
>>> type(1)

1 dengan periode setelah itu adalah float:

>>> 1.
1.0
>>> type(1.)

Titik berikutnya dengan sendirinya akan menjadi SyntaxError, tapi itu mulai putus-putus lookup pada contoh float:

>>> 1..__truediv__

Tidak ada orang lain telah disebutkan ini Ini "terikat metode" di the float, 1.0:

>>> f = 1..__truediv__
>>> f

>>> f(2)
0.5
>>> f(3)
0.33333333333333331

Kita bisa mencapai fungsi yang sama jauh lebih readably:

>>> def divide_one_by(x):
...     return 1.0/x
...     
>>> divide_one_by(2)
0.5
>>> divide_one_by(3)
0.33333333333333331

Kinerja

Kelemahan dari divide_one_by fungsi adalah bahwa hal itu memerlukan yang lain Python stack frame, sehingga agak lebih lambat daripada terikat metode:

>>> 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]

Tentu saja, jika anda hanya bisa menggunakan plain literal, yang's bahkan lebih cepat:

>>> def f_3():
...     for x in range(1, 11):
...         1.0/x
...         
>>> timeit.repeat(f_3)
[2.1224895628296281, 2.1219930218637728, 2.1280188256941983]
Komentar (0)