Câu trả lời:
lenlà một hàm để lấy độ dài của một tập hợp. Nó hoạt động bằng cách gọi __len__phương thức của một đối tượng .__something__các thuộc tính đặc biệt và thường hơn là bắt mắt, và thường không nên được gọi trực tiếp.
Nó đã được quyết định vào một thời điểm nào đó từ lâu rằng độ dài của một cái gì đó phải là một hàm chứ không phải mã phương thức, lập luận rằng len(a)ý nghĩa của nó sẽ rõ ràng đối với người mới bắt đầu nhưng a.len()sẽ không rõ ràng như vậy. Khi Python bắt đầu __len__thậm chí còn không tồn tại và lenlà một thứ đặc biệt hoạt động với một vài loại đối tượng. Cho dù tình huống này có khiến chúng ta hoàn toàn hợp lý hay không, thì nó vẫn ở đây.
Thường xảy ra trường hợp hành vi "điển hình" của một toán tử hoặc toán tử cài sẵn là gọi (với cú pháp khác và đẹp hơn) các phương thức ma thuật phù hợp (những phương thức có tên như vậy __whatever__) trên các đối tượng liên quan. Thường thì toán tử hoặc toán tử được tích hợp sẵn có "giá trị gia tăng" (nó có thể đi theo các đường dẫn khác nhau tùy thuộc vào các đối tượng liên quan) - trong trường hợp lenvs __len__, nó chỉ là một chút kiểm tra tỉnh táo về cài đặt sẵn bị thiếu trong phương pháp ma thuật:
>>> class bah(object):
... def __len__(self): return "an inch"
...
>>> bah().__len__()
'an inch'
>>> len(bah())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object cannot be interpreted as an integer
Khi bạn thấy một lệnh gọi tích lenhợp sẵn, bạn chắc chắn rằng, nếu chương trình tiếp tục sau đó thay vì đưa ra một ngoại lệ, thì lệnh gọi đã trả về một số nguyên, không âm và nhỏ hơn 2 ** 31 - khi bạn thấy một cuộc gọi đếnxxx.__len__() , bạn không có gì chắc chắn (ngoại trừ tác giả của mã hoặc không quen với Python hoặc không tốt ;-).
Các tích hợp sẵn khác thậm chí còn cung cấp nhiều giá trị gia tăng hơn ngoài việc kiểm tra sự tỉnh táo đơn giản và khả năng đọc. Bằng cách thiết kế thống nhất tất cả Python để hoạt động thông qua các cuộc gọi đến nội trang và sử dụng các toán tử, không bao giờ thông qua các lệnh gọi đến các phương thức ma thuật, các lập trình viên không phải nhớ gánh nặng đó là trường hợp nào. (Đôi khi lỗi xảy ra: cho đến 2.5, bạn phải gọi foo.next()- trong 2.6, trong khi điều đó vẫn hoạt động để tương thích ngược, bạn nên gọi next(foo)và trong 3.*, magic method được đặt tên chính xác __next__thay vì "oops-ey"next ! - ).
Vì vậy, quy tắc chung là không bao giờ gọi một phương thức ma thuật trực tiếp (nhưng luôn gián tiếp thông qua một phương thức cài sẵn) trừ khi bạn biết chính xác lý do tại sao bạn cần làm điều đó (ví dụ: khi bạn ghi đè một phương thức như vậy trong một lớp con, nếu lớp con cần chuyển sang lớp cha phải được thực hiện thông qua lệnh gọi rõ ràng tới phương thức ma thuật).
def len(x): return "I am a string." print(len(42)) print(len([1,2,3]))và nó được in I am stringhai lần. Bạn có thể giải thích thêm được không?
__len__ đặc biệt (không phải hàm) trên đối tượng đang được xem xét.
lenchứ không phải một số hàm khác (như trong ví dụ của tôi) tình cờ có cùng tên - len. Không có cảnh báo như "Bạn đang xác định lại hàm len được tích hợp sẵn" hoặc đại loại như thế này. Theo tôi, tôi không thể chắc chắn về những gì Alex đã nêu trong câu trả lời của mình.
len in vars(__builtins__).values().
Bạn có thể coi len () gần tương đương với
def len(x):
return x.__len__()
Một lợi thế là nó cho phép bạn viết những thứ như
somelist = [[1], [2, 3], [4, 5, 6]]
map(len, somelist)
thay vì
map(list.__len__, somelist)
hoặc là
map(operator.methodcaller('__len__'), somelist)
Có một chút hành vi khác nhau mặc dù. Ví dụ trong trường hợp ints
>>> (1).__len__()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'int' object has no attribute '__len__'
>>> len(1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: object of type 'int' has no len()
operator.methodcallerthay vì operator.attrgetter.
Bạn có thể kiểm tra tài liệu Pythond :
>>> class Meta(type):
... def __getattribute__(*args):
... print "Metaclass getattribute invoked"
... return type.__getattribute__(*args)
...
>>> class C(object):
... __metaclass__ = Meta
... def __len__(self):
... return 10
... def __getattribute__(*args):
... print "Class getattribute invoked"
... return object.__getattribute__(*args)
...
>>> c = C()
>>> c.__len__() # Explicit lookup via instance
Class getattribute invoked
10
>>> type(c).__len__(c) # Explicit lookup via type
Metaclass getattribute invoked
10
>>> len(c) # Implicit lookup
10