`1 ..__ truediv__` nedir? Python'un .. (“dot dot”) gösterim sözdizimi var mı?


190

Son zamanlarda python öğrendiğimde daha önce hiç görmediğim bir sözdizimiyle veya çoğu öğreticide, ..gösterimde, böyle bir şeyle karşılaştım:

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

print(f(8)) # prints 0.125 

Ben tam olarak aynı olduğunu düşündüm (tabii ki, daha uzun hariç):

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

Ama sorularım:

  • Bunu nasıl yapabilir?
  • İki nokta için aslında ne anlama geliyor?
  • Daha karmaşık bir ifadede (mümkünse) nasıl kullanabilirsiniz?

Bu muhtemelen bana gelecekte çok sayıda kod satırı kurtaracak ... :)


14
Not: Birincisinin yaptığı önceki çağrılarla (1).__truediv__gerçekten aynı değildir . Alternatif olarak, (boşluklu) kullanabilirsiniz1..__truediv__int.__truediv__float.__truediv__1 .__truediv__
tobias_k

7
Not 1//8olduğu 0değil, 0.125Python ya sürümünde.
mkrieger1

1
bana hatırlatıyorif (x <- 3) {...}
Dunno

7
İşte kullanımda bunun bir örneği.
Éamonn Olive

3
@KeithC Yüksek kaliteli cevaplar ve yorumlar, örnek kodun kavramak için kavrayışa ihtiyaç duyduğunu, birçoğu için şaşırtıcı olduğunu, daha net, daha genel ve en azından verimli alternatiflere sahip olduğunu göstermektedir. Benim asıl sorunum, okunabilirliğin önemli olduğudur. İnsanlarla iletişim kurarak en çok ihtiyaç duyulan yer için zekayı koruyun.
Peter Wood

Yanıtlar:


212

Sahip olduğunuz, floatdaha sonra __truediv__yönteme eriştiğiniz sıfırdan bağımsız bir değişmez değerdir . Kendi başına bir operatör değil; ilk nokta kayan değerin bir parçasıdır ve ikinci nokta nesnelerin özelliklerine ve yöntemlerine erişmek için nokta operatörüdür.

Aşağıdakileri yaparak aynı noktaya ulaşabilirsiniz.

>>> f = 1.
>>> f
1.0
>>> f.__floordiv__
<method-wrapper '__floordiv__' of float object at 0x7f9fb4dc1a20>

Başka bir örnek

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

Burada 1.0'a 2.0 ekliyoruz, bu da 3.0'ı veriyor.


165
Bulduğumuz şey, küçük bir kısalık için çok açıklıktan fedakarlık eden bir geliştiricidir ve işte buradayız.
TemporalWolf

11
Belki birisi kaynak kodunu 5.5 "diskete kaydediyor?
Thomas Ayoub

10
@ThomasAyoub 5.25 "iirc ;-)
jjmontes

9
@TemporalWolf Bu son kod golf başvurusunda bulmuş olabilir .
Brian McCutchon

2
Eğlenceli gerçek, bunu JavaScript ile de yapabilirsiniz:1..toString()
Derek 朕 會 功夫

74

Soru zaten yeterince cevaplanmıştır (yani @Paul Rooney'nin cevabı), ancak bu cevapların doğruluğunu doğrulamak da mümkündür.

Mevcut cevapları özetleyeyim: ..Tek bir sözdizimi öğesi değil!

Kaynak kodun nasıl "tokenleştirildiğini" kontrol edebilirsiniz . Bu belirteçler kodun nasıl yorumlandığını gösterir:

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

Dize 1.sayı olarak yorumlanır, ikincisi .bir OP (bir operatör, bu durumda "get attribute" operatörü) ve __truediv__yöntem adıdır. Yani bu sadece __truediv__şamandıra yöntemine erişiyor 1.0.

Oluşturulan bayt kodunu görüntülemenin bir başka yolu, onu birleştirmektir . Bu aslında bir kod yürütüldüğünde gerçekleştirilen talimatları gösterir: 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

Bu temelde aynı şeyi söylüyor. Bu özellik yükler __truediv__sabiti 1.0.


Sorunuzla ilgili

Ve daha karmaşık bir ifadede (mümkünse) nasıl kullanabilirsiniz?

Mümkün olsa da asla böyle bir kod yazmamalısınız, çünkü kodun ne yaptığı belirsizdir. Bu yüzden lütfen daha karmaşık ifadelerde kullanmayın. Şimdiye kadar bu kadar basit ifadelerde kullanmamalısınız, en azından talimatları ayırmak için parantez kullanmalısınız:

f = (1.).__truediv__

bu kesinlikle daha okunaklı olacaktır - ancak şu satırlarda bir şey olabilir:

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

daha iyi olurdu!

Kullanımı yaklaşımı , bu küçük snippet tarafından gösterilebilen python'un veri modelinipartial de korur ( yaklaşım öyle değildir!):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

Bunun nedeni ise 1. / (1+2j)dikkate alınmamaktadır, float.__truediv__fakat complex.__rtruediv__- operator.truedivEmin ters işlem denir yaptığında normal çalışma döner NotImplementedancak ameliyat sırasında bu yedeklerini yok __truediv__doğrudan. Bu "beklenen davranış" kaybı (normal olarak) sihirli yöntemleri doğrudan kullanmamanızın ana nedenidir.


40

İki nokta birlikte ilk başta biraz garip olabilir:

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

Ama yazmakla aynı:

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

Çünkü floatdeğişmez değerler üç biçimde yazılabilir:

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

Bu şaşırtıcı, neden bu geçerli sözdizimi, ama 1.__truediv__değil?
Alex Hall

3
Bkz @AlexHall burada . .Sayısının bir parçası olarak çözümlenir gibi görünüyor ve sonra .yöntem erişgeç eksik.
tobias_k

7
Ancak garip ve belirsiz bir sözdizimi olduğundan, muhtemelen kaçınılmalıdır.
DrMcCleod

11

Nedir f = 1..__truediv__?

fdeğeri bir şamandıra üzerinde bağlı özel bir yöntemdir. özellikle,

1.0 / x

Python 3'te:

(1.0).__truediv__(x)

Kanıt:

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

ve:

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

Eğer yaparsak:

f = one.__truediv__

Bu bağlı yönteme bağlı bir ad tutuyoruz

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

Eğer o noktalı aramayı sıkı bir döngü içinde yapsaydık, bu biraz zaman kazandırabilirdi.

Soyut Sözdizimi Ağacının (AST) Ayrıştırılması

İfade için AST'yi ayrıştırmanın bize __truediv__kayan nokta sayısı niteliğini aldığımızı söylediğini görebiliriz 1.0:

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

Aynı sonuç fonksiyonunu şuradan alabilirsiniz:

f = float(1).__truediv__

Veya

f = (1.0).__truediv__

kesinti

Ayrıca oraya kesinti ile ulaşabiliriz.

Hadi inşa edelim.

1 tek başına int:

>>> 1
1
>>> type(1)
<type 'int'>

1 float olduktan sonraki bir süre ile:

>>> 1.
1.0
>>> type(1.)
<type 'float'>

Sonraki nokta tek başına bir SyntaxError olur, ancak kayan nokta örneğinde noktalı bir arama başlatır:

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

Başka hiç kimse bu sözü var - Bu artık bir olduğunu "bağımlı yöntem" , şamandıra üzerinde 1.0:

>>> f = 1..__truediv__
>>> f
<method-wrapper '__truediv__' of float object at 0x127F3CD8>
>>> f(2)
0.5
>>> f(3)
0.33333333333333331

Aynı işlevi çok daha okunaklı bir şekilde başarabiliriz:

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

Verim

divide_one_byİşlevin dezavantajı, başka bir Python yığın çerçevesi gerektirmesi ve onu bağlı yöntemden biraz daha yavaş hale getirmesidir:

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

Tabii ki, sadece düz değişmezleri kullanabiliyorsanız, bu daha da hızlıdır:

>>> def f_3():
...     for x in range(1, 11):
...         1.0/x
...         
>>> timeit.repeat(f_3)
[2.1224895628296281, 2.1219930218637728, 2.1280188256941983]
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.