Mengapa kode Python menggunakan fungsi len () alih-alih metode panjang?

204

Saya tahu bahwa python memiliki len()fungsi yang digunakan untuk menentukan ukuran string, tetapi saya bertanya-tanya mengapa itu bukan metode objek string.

Memperbarui

Oke, saya sadar saya salah. __len__()sebenarnya adalah metode objek string. Tampaknya aneh melihat kode berorientasi objek dengan Python menggunakan fungsi len pada objek string. Lebih jauh lagi, itu juga aneh untuk melihat __len__sebagai nama bukan hanya len.

fuentesjr
sumber

Jawaban:

180

String memiliki metode panjang: __len__()

Protokol dengan Python adalah untuk mengimplementasikan metode ini pada objek yang memiliki panjang dan menggunakan fungsi built-in len(), yang memanggilnya untuk Anda, mirip dengan cara Anda mengimplementasikan __iter__()dan menggunakan fungsi built-in iter()(atau memiliki metode yang dipanggil di belakang adegan untuk Anda) pada objek yang dapat diubah.

Lihat Meniru jenis wadah untuk informasi lebih lanjut.

Berikut ini adalah bacaan yang bagus tentang subjek protokol dengan Python: Python dan Prinsip Least Astonishment

Jonny Buchanan
sumber
124
Ini mengejutkan saya betapa bodohnya alasan penggunaannya len. Mereka berpikir bahwa lebih mudah untuk memaksa orang untuk mengimplementasikan .__len__daripada memaksa orang untuk menerapkan .len(). Itu hal yang sama, dan yang terlihat jauh lebih bersih. Jika bahasanya akan memiliki OOP __len__, apa tujuan dunia inilen(..)
alternatif
38
len, str, dll. dapat digunakan dengan fungsi tingkat tinggi seperti memetakan, mengurangi, dan memfilter tanpa perlu mendefinisikan fungsi atau lambda hanya untuk memanggil metode. Tidak semuanya berputar di sekitar OOP, bahkan dengan Python.
Evicatos
11
Juga, dengan menggunakan protokol, mereka dapat memberikan cara alternatif untuk mengimplementasikan berbagai hal. Misalnya, Anda bisa membuat iterable dengan __iter__, atau hanya dengan __getitem__, dan iter(x)akan berfungsi dengan baik. Anda dapat membuat objek konteks yang dapat digunakan dengan __bool__atau __len__, dan bool(x)akan bekerja dengan baik. Dan seterusnya. Saya pikir Armin menjelaskan hal ini dengan cukup baik di posting terkait - tetapi bahkan jika dia tidak, memanggil Python bodoh karena penjelasan luar oleh seorang pria yang sering secara publik berselisih dengan para dev inti tidak akan adil ...
abarnert
8
@Evicatos: +1 untuk "Tidak semuanya berputar di sekitar OOP", tapi saya akan mengakhiri dengan " terutama dengan Python", bahkan tidak . Python (tidak seperti Java, Ruby, atau Smalltalk) tidak mencoba menjadi bahasa "OOP murni"; itu secara eksplisit dirancang untuk menjadi bahasa "multi-paradigma". Pemahaman daftar bukan metode di iterables. zipadalah fungsi tingkat atas, bukan zip_withmetode. Dan seterusnya. Hanya karena segala sesuatu adalah objek tidak berarti menjadi objek adalah hal yang paling penting tentang setiap hal.
abarnert
7
Artikel itu ditulis dengan sangat buruk sehingga saya merasa bingung dan marah setelah mencoba membacanya. "Dalam Python 2.x tipe Tuple misalnya tidak mengekspos metode non-khusus dan Anda dapat menggunakannya untuk membuat string dari itu:" Semuanya adalah penggabungan dari kalimat yang hampir tidak dapat diuraikan.
MirroredFate
93

Jawaban Jim untuk pertanyaan ini dapat membantu; Saya salin di sini. Mengutip Guido van Rossum:

Pertama-tama, saya memilih len (x) daripada x.len () karena alasan HCI (def __len __ () muncul jauh kemudian). Sebenarnya ada dua alasan yang saling terkait, keduanya HCI:

(a) Untuk beberapa operasi, notasi awalan hanya membaca lebih baik daripada postfix - awalan (dan infiks!) operasi memiliki tradisi panjang dalam matematika yang menyukai notasi di mana visual membantu ahli matematika berpikir tentang suatu masalah. Bandingkan yang mudah dengan yang kita tulis ulang rumus seperti x * (a + b) menjadi x a + x b dengan kecanggungan melakukan hal yang sama menggunakan notasi OO mentah.

(B) Ketika saya membaca kode yang mengatakan len (x) saya tahu bahwa itu meminta panjang sesuatu. Ini memberi tahu saya dua hal: hasilnya adalah bilangan bulat, dan argumennya adalah semacam wadah. Sebaliknya, ketika saya membaca x.len (), saya harus sudah tahu bahwa x adalah semacam wadah yang mengimplementasikan antarmuka atau mewarisi dari kelas yang memiliki standar len (). Saksikan kebingungan yang kadang-kadang kita miliki ketika kelas yang tidak mengimplementasikan pemetaan memiliki metode get () atau keys (), atau sesuatu yang bukan file memiliki metode write ().

Mengatakan hal yang sama dengan cara lain, saya melihat 'len' sebagai operasi bawaan. Aku benci kehilangan itu. / ... /

Federico A. Ramponi
sumber
37

Ada lenmetode:

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

Python adalah bahasa pemrograman pragmatis, dan alasan untuk len()menjadi fungsi dan bukan metode str, list, dictdll pragmatis.

Fungsi len()bawaan berurusan langsung dengan tipe bawaan: implementasi CPython len()sebenarnya mengembalikan nilai ob_sizebidang dalam PyVarObjectC struct yang mewakili objek bawaan berukuran variabel dalam memori. Ini jauh lebih cepat daripada memanggil metode - tidak perlu pencarian atribut. Mendapatkan jumlah item dalam koleksi adalah operasi umum dan harus bekerja secara efisien untuk tipe dasar dan beragam seperti str, list, array.arraydll

Namun, untuk mempromosikan konsistensi, saat melamar len(o)ke tipe yang ditentukan pengguna, Python memanggil o.__len__()sebagai fallback. __len__, __abs__dan semua metode khusus lainnya yang didokumentasikan dalam Model Data Python memudahkan untuk membuat objek yang berperilaku seperti bawaan, memungkinkan API ekspresif dan sangat konsisten yang kita sebut "Pythonic".

Dengan menerapkan metode khusus, objek Anda dapat mendukung iterasi, membebani operator infiks, mengelola konteks dalam withblok, dll. Anda dapat menganggap Model Data sebagai cara menggunakan bahasa Python itu sendiri sebagai kerangka kerja di mana objek yang Anda buat dapat diintegrasikan dengan mulus.

Alasan kedua, didukung oleh kutipan dari Guido van Rossum seperti ini , adalah lebih mudah untuk membaca dan menulis len(s)daripada s.len().

Notasi len(s)konsisten dengan operator unary dengan notasi awalan, seperti abs(n). len()digunakan lebih sering daripada abs(), dan layak untuk mudah ditulis.

Mungkin juga ada alasan historis: dalam bahasa ABC yang mendahului Python (dan sangat berpengaruh dalam desainnya), ada operator unary yang ditulis sebagaimana #sdimaksud len(s).

Luciano Ramalho
sumber
12
met% python -c 'import this' | grep 'only one'
There should be one-- and preferably only one --obvious way to do it.
Alex Coventry
sumber
34
Hal yang jelas ketika bekerja dengan objek, tentu saja, metode.
Peter Cooper
4
@ Peter: Saya akan membayar $ 20 kepada siapa pun dengan bukti foto bahwa mereka menempelkan komentar Anda di punggung Guido. $ 50 jika ada di dahinya.
bug
1
Ya, desainer Python mematuhi dogma tetapi mereka sendiri tidak menghormati dogma mereka sendiri.
bitek
2
$ python -c 'impor ini' | grep jelas
Ed Randall
4

Ada beberapa jawaban yang bagus di sini, dan sebelum saya memberikan milik saya, saya ingin menyoroti beberapa permata (tidak ada kata ruby ​​yang dimaksudkan) yang saya baca di sini.

  • Python bukan bahasa OOP murni - ini adalah tujuan umum, bahasa multi-paradigma yang memungkinkan programmer untuk menggunakan paradigma yang paling mereka sukai dan / atau paradigma yang paling cocok untuk solusi mereka.
  • Python memiliki fungsi kelas satu, jadi lensebenarnya adalah objek. Ruby, di sisi lain, tidak memiliki fungsi kelas satu. Jadi lenobjek fungsi memiliki metode sendiri yang dapat Anda periksa dengan menjalankan dir(len).

Jika Anda tidak suka cara ini bekerja dalam kode Anda sendiri, itu sepele bagi Anda untuk mengimplementasikan kembali wadah menggunakan metode yang Anda sukai (lihat contoh di bawah).

>>> 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
Charles Addis
sumber
0

Bisa juga dikatakan

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

Menggunakan Python 2.7.3.

SpanosAngelos
sumber
9
lenfungsi sudah disebutkan dalam jawaban sebelumnya. Apa gunanya "jawaban" ini?
Piotr Dobrogost
0

Sesuatu yang hilang dari sisa jawaban di sini: lenfungsi memeriksa bahwa __len__metode mengembalikan non-negatif int. Fakta yang lenmerupakan fungsi berarti bahwa kelas tidak bisa mengesampingkan perilaku ini untuk menghindari pemeriksaan. Dengan demikian, len(obj)memberikan tingkat keamanan yang obj.len()tidak bisa.

Contoh:

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

Tentu saja, dimungkinkan untuk "menimpa" lenfungsi dengan menugaskannya kembali sebagai variabel global, tetapi kode yang melakukan ini jauh lebih mencurigakan daripada kode yang menimpa metode di kelas.

kaya3
sumber
-1

Tidak?

>>> "abc".__len__()
3
Nick Stinemates
sumber