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:
Ini mungkin akan menghemat saya banyak baris kode di masa depan...:)
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__
<method-wrapper '__floordiv__' of float object at 0x7f9fb4dc1a20>
Contoh lain
>>> 1..__add__(2.)
3.0
Di sini kita tambahkan 1.0 ke 2.0, yang jelas hasil 3.0.
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.
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
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.
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__
Kita juga bisa mendapatkan di sana dengan deduksi.
Let's membangunnya.
1 dengan sendirinya adalah sebuah int
:
>>> 1
1
>>> type(1)
<type 'int'>
1 dengan periode setelah itu adalah float:
>>> 1.
1.0
>>> type(1.)
<type 'float'>
Titik berikutnya dengan sendirinya akan menjadi SyntaxError, tapi itu mulai putus-putus lookup pada contoh float:
>>> 1..__truediv__
<method-wrapper '__truediv__' of float object at 0x0D1C7BF0>
Tidak ada orang lain telah disebutkan ini Ini "terikat metode" di the float, 1.0
:
>>> f = 1..__truediv__
>>> f
<method-wrapper '__truediv__' of float object at 0x127F3CD8>
>>> 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
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]