Python kodu neden uzunluk yöntemi yerine len () işlevini kullanıyor?


204

Python len()bir dize boyutunu belirlemek için kullanılan bir işlevi olduğunu biliyorum , ama neden dize nesnesinin bir yöntemi olmadığını merak ediyordum.

Güncelleme

Tamam, utanç verici bir şekilde yanıldığımı fark ettim. __len__()aslında bir dize nesnesinin yöntemidir. Dize nesneleri üzerinde len işlevini kullanarak Python'da nesneye yönelik kod görmek garip görünüyor. Ayrıca, __len__sadece len yerine isim olarak görmek de garip .

Yanıtlar:


180

Dizelerin uzunluk yöntemi vardır: __len__()

Python'daki protokol, bu yöntemi, uzunluğa sahip olan ve yerleşik len()işlevi __iter__()kullanan, yerleşik iter()işlevi kullanan ve yerleşik işlevi kullanma ve kullanma yönteminize (veya arkasındaki yönteme sahip) benzer nesnelere uygulamaktır. sizin için sahneler).

Bkz in taklit konteyner türleri daha fazla bilgi için.

İşte Python'daki protokoller konusunda iyi bir okuma: Python ve En Az Şaşkınlık İlkesi


124
Kullanım nedeninin ne kadar moronik olduğunu beni hayrete düşürüyor len. Onlar uygulamak için insanları zorlamak için daha kolay olduğunu düşünüyorum .__len__uygulamak için kuvvet insanlara daha .len(). Aynı şey ve biri çok daha temiz görünüyor . Dilin bir OOP'si olacaksa __len__, dünyada ne yapmak gerekirlen(..)
alternatif

38
len, str vb. sadece bir yöntemi çağırmak için bir fonksiyon veya lambda tanımlamaya gerek kalmadan harita, azaltma ve filtreleme gibi daha üst düzey fonksiyonlarla kullanılabilir. Python'da bile her şey OOP etrafında dönmez.
Evicatos

11
Ayrıca, bir protokol kullanarak, şeyleri uygulamak için alternatif yollar sağlayabilirler. Örneğin __iter__, bir veya sadece bir yinelenebilir oluşturabilir __getitem__ve iter(x)her iki şekilde de çalışabilirsiniz. Sen ile kullanılabilir bir-in-bool-bağlam nesnesi oluşturabilir __bool__ya __len__ve bool(x)her iki şekilde çalışacaktır. Ve bunun gibi. Sanırım Armin bunu bağlantılı yazıda oldukça iyi açıklıyor - ama olmasa bile, Python moronic'i çağıran, çekirdek geliştiricilerle genellikle
aleyhte

8
@Evicatos: "Her şey OOP etrafında dönmez" için +1, ancak " özellikle Python'da" ile biterdim , hatta . Python (Java, Ruby veya Smalltalk'ın aksine) "saf OOP" dili olmaya çalışmaz; açıkça bir "çok paradigma" dili olacak şekilde tasarlanmıştır. Liste kavrayışı yinelenebilir yöntemlerle ilgili bir yöntem değildir. zipbir üst düzey işlevdir, bir zip_withyöntem değildir . Ve bunun gibi. Her şeyin bir nesne olması, nesne olmanın her şeyle ilgili en önemli şey olduğu anlamına gelmez.
abarnert

7
Bu makale o kadar kötü yazılmış ki, okumaya çalıştıktan sonra şaşkın ve kızgın hissediyorum. "Python 2.x'de Tuple türü, özel olmayan herhangi bir yöntemi ortaya çıkarmaz ve yine de bunu bir dize yapmak için kullanabilirsiniz:" Her şey, neredeyse çözülemeyen cümlelerin birleşmesidir.
MirroredFate

93

Jim'in bu soruya cevabı yardımcı olabilir; Onu buraya kopyalarım. Guido van Rossum'dan alıntı:

Her şeyden önce, HCI nedenlerinden dolayı len.x'i x.len () yerine seçtim (def __len __ () çok sonra geldi). Aslında iç içe geçmiş iki neden vardır, her ikisi de HCI:

(a) Bazı işlemler için, önek notasyonu postfix'den daha iyi okunur - önek (ve infix!) işlemleri matematikte uzun bir geleneğe sahiptir ve bu görsellerin matematikçinin bir problem hakkında düşünmesine yardımcı olduğu gösterimleri sever. X içerisine dahil x * (a + b) gibi bir formül yeniden hangi kolay karşılaştır a + x ham OO gösterimi kullanılarak aynı şeyi hantallık için b.

(b) len (x) yazan kodu okuduğumda bir şeyin uzunluğunu istediğini biliyorum. Bu bana iki şeyi anlatıyor: sonuç bir tamsayı ve argüman bir çeşit kap. Aksine, x.len () 'i okuduğumda, x'in bir arayüz uygulayan veya standart len ​​() olan bir sınıftan miras alan bir tür kap olduğunu zaten bilmeliyim. Eşleme uygulamayan bir sınıfın get () veya keys () yöntemine veya dosya olmayan bir şeyin bir write () yöntemine sahip olması durumunda zaman zaman yaşadığımız karışıklığa tanık olun.

Aynı şeyi başka bir şekilde söyleyerek, 'len' yi yerleşik bir işlem olarak görüyorum. Bunu kaybetmekten nefret ederim. / ... /


37

Bir lenyöntem var:

>>> a = 'a string of some length'
>>> a.__len__()
23
>>> a.__len__
<method-wrapper '__len__' of str object at 0x02005650>

34

Python pragmatik bir programlama dilidir ve nedenleri len()bir işlev değil, yöntemi olmaktan str, list, dictvb pragmatik vardır.

len()Yerleşik işlev fırsatlar doğrudan birlikte yerleşik türleri: ait CPython uygulaması len()aslında değerini verir ob_sizealanda PyVarObjectC struct oluşturan hiçbir değişken büyüklükte dahili bellekte nesne. Bu, bir yöntemi çağırmaktan çok daha hızlıdır; özellik aramasına gerek yoktur. Bir koleksiyondaki öğe sayısını alınıyor ortak bir işlemdir ve gibi temel ve farklı türleri için verimli çalışması gerektiğini str, list, array.arrayvb

Bununla birlikte, tutarlılığı artırmak len(o)için, kullanıcı tanımlı bir türe başvururken , Python o.__len__()bir geri dönüş olarak adlandırır . __len__, __abs__Belgelenen ve diğer tüm özel yöntemler Python Veri Modeli bu kadar kolay inşa işlemleri gibi davranırlar, biz "Pythonic" dediğimiz anlamlı ve son derece tutarlı API'leri sağlayan nesneleri oluşturmak için yapmak.

Nesneleriniz özel yöntemleri uygulayarak yinelemeyi, aşırı yük infix operatörlerini destekleyebilir, withbloklar içindeki bağlamları yönetebilir .

Gibi Guido van Rossum alıntılarla tarafından desteklenen ikinci bir neden, bu bir , okuma ve yazma daha kolay olmasıdır len(s)daha s.len().

Gösterim len(s), önek gösterimine sahip tekli operatörlerle tutarlıdır abs(n). len()daha sık kullanılır abs()ve yazılması kolay olmayı hak eder.

Tarihsel bir neden de olabilir: Python'dan önce gelen (ve tasarımında çok etkili olan) ABC dilinde, #sanlamı olarak yazılmış tek bir operatör vardı len(s).


12
met% python -c 'import this' | grep 'only one'
There should be one-- and preferably only one --obvious way to do it.

34
Bir nesne ile çalışırken elbette bir yöntem var.
Peter Cooper

4
@Peter: Fotoğraf kanıtı olan herkese yorumunuzu Guido'nun sırtına bantladıklarını 20 dolar ödeyeceğim. Alnında ise 50 dolar.
böcek

1
Evet, Python tasarımcıları dogmaya bağlı kalırlar ama kendileri kendi dogmalarına saygı duymazlar.
bitek

2
$ python -c 'bunu içe aktar' | grep bariz
Ed Randall

4

Burada bazı harika cevaplar var ve bu yüzden kendime vermeden önce burada okuduğum bazı mücevherleri vurgulamak istiyorum (yakut punta yok).

  • Python saf bir OOP dili değildir - programcının en rahat oldukları paradigmayı ve / veya çözümlerine en uygun paradigmayı kullanmasına izin veren genel amaçlı, çok paradigmalı bir dildir.
  • Python birinci sınıf işlevlere sahiptir, bu yüzden lenaslında bir nesnedir. Öte yandan Ruby, birinci sınıf işlevlere sahip değildir. Böylece lenfonksiyon nesnesinin çalıştırarak inceleyebileceğiniz kendi yöntemleri vardır dir(len).

Bunun kendi kodunuzda çalışma şeklini beğenmezseniz, kapları tercih ettiğiniz yöntemi kullanarak yeniden uygulamanız önemsizdir (aşağıdaki örneğe bakın).

>>> class List(list):
...     def len(self):
...         return len(self)
...
>>> class Dict(dict):
...     def len(self):
...         return len(self)
...
>>> class Tuple(tuple):
...     def len(self):
...         return len(self)
...
>>> class Set(set):
...     def len(self):
...         return len(self)
...
>>> my_list = List([1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'])
>>> my_dict = Dict({'key': 'value', 'site': 'stackoverflow'})
>>> my_set = Set({1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'})
>>> my_tuple = Tuple((1,2,3,4,5,6,7,8,9,'A','B','C','D','E','F'))
>>> my_containers = Tuple((my_list, my_dict, my_set, my_tuple))
>>>
>>> for container in my_containers:
...     print container.len()
...
15
2
15
15

0

Ayrıca söyleyebilirsin

>> x = 'test'
>> len(x)
4

Python Kullanımı 2.7.3.


9
lenişlev önceki yanıtlarda zaten belirtilmişti. Peki bu "cevabın" anlamı nedir?
Piotr Dobrogost

0

Buradaki yanıtların geri kalanında eksik olan bir şey var: lenişlev, __len__yöntemin negatif olmayan bir değer döndürüp döndürmediğini kontrol eder int. Bu lenbir işlev olması, sınıfların denetimi önlemek için bu davranışı geçersiz kılamayacağı anlamına gelir. Bu nedenle, yapamayan bir len(obj)güvenlik seviyesi verir obj.len().

Misal:

>>> class A:
...     def __len__(self):
...         return 'foo'
...
>>> len(A())
Traceback (most recent call last):
  File "<pyshell#8>", line 1, in <module>
    len(A())
TypeError: 'str' object cannot be interpreted as an integer
>>> class B:
...     def __len__(self):
...         return -1
... 
>>> len(B())
Traceback (most recent call last):
  File "<pyshell#13>", line 1, in <module>
    len(B())
ValueError: __len__() should return >= 0

Elbette, lenişlevi global bir değişken olarak yeniden atayarak işlevi "geçersiz kılmak" mümkündür , ancak bunu yapan kod, bir sınıftaki bir yöntemi geçersiz kılan koddan çok daha şüphelidir.


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.