มีความแตกต่างระหว่างการโทรlen([1,2,3])หรือ[1,2,3].__len__()?
หากไม่มีความแตกต่างที่มองเห็นได้จะมีการทำอะไรที่แตกต่างกันเบื้องหลัง?
มีความแตกต่างระหว่างการโทรlen([1,2,3])หรือ[1,2,3].__len__()?
หากไม่มีความแตกต่างที่มองเห็นได้จะมีการทำอะไรที่แตกต่างกันเบื้องหลัง?
คำตอบ:
lenคือฟังก์ชั่นรับความยาวของคอลเลกชัน มันทำงานโดยเรียก__len__เมธอดของวัตถุ __something__แอตทริบิวต์มีความพิเศษและมักจะตรงตามสายตาและโดยทั่วไปไม่ควรเรียกโดยตรง
มีการตัดสินใจเมื่อนานมาแล้วการกำหนดความยาวของบางสิ่งควรเป็นฟังก์ชันไม่ใช่รหัสวิธีการโดยให้เหตุผลว่าความlen(a)หมายของผู้เริ่มต้นจะชัดเจน แต่a.len()จะไม่ชัดเจนเท่าที่ควร เมื่อ Python เริ่ม__len__ไม่มีอยู่จริงและlenเป็นสิ่งพิเศษที่ใช้ได้กับออบเจ็กต์บางประเภท ไม่ว่าสถานการณ์นี้จะทำให้เราสมเหตุสมผลหรือไม่ก็ตามก็อยู่ที่นี่ต่อไป
มักจะเป็นกรณีที่พฤติกรรม "ทั่วไป" ของบิวท์อินหรือตัวดำเนินการคือการเรียก (ด้วยไวยากรณ์ที่แตกต่างและดีกว่า) วิธีการใช้เวทมนตร์ที่เหมาะสม (ที่มีชื่อเหมือน__whatever__) บนวัตถุที่เกี่ยวข้อง บ่อยครั้งที่ตัวดำเนินการในตัวหรือตัวดำเนินการมี "มูลค่าเพิ่ม" (สามารถใช้เส้นทางที่แตกต่างกันได้ขึ้นอยู่กับวัตถุที่เกี่ยวข้อง) - ในกรณีของlenvs __len__มันเป็นเพียงการตรวจสอบความมีสติในตัวที่ขาดหายไปจาก วิธีมายากล:
>>> 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
เมื่อคุณเห็นการโทรไปยังlenบิวท์อินคุณแน่ใจว่าหากโปรแกรมดำเนินการต่อหลังจากนั้นแทนที่จะเพิ่มข้อยกเว้นการโทรจะส่งคืนจำนวนเต็มไม่เป็นลบและน้อยกว่า 2 ** 31 - เมื่อ คุณเห็นการโทรหาxxx.__len__()คุณไม่มีความมั่นใจ (ยกเว้นว่าผู้เขียนโค้ดไม่คุ้นเคยกับ Python หรือไม่ดี ;-)
บิวท์อินอื่น ๆ ให้มูลค่าเพิ่มที่มากกว่าการตรวจสอบความถูกต้องและการอ่านค่า ด้วยการออกแบบ Python ทั้งหมดอย่างสม่ำเสมอเพื่อให้ทำงานผ่านการโทรไปยังบิวด์อินและการใช้งานตัวดำเนินการไม่ผ่านการเรียกใช้วิธีการใช้เวทมนตร์โปรแกรมเมอร์จะไม่ต้องจำภาระในการจดจำว่ากรณีใดเป็นกรณีใด (บางครั้งข้อผิดพลาดหลุดเข้ามา: จนถึง 2.5 คุณต้องโทรfoo.next()- ใน 2.6 ในขณะที่ยังคงใช้งานได้เพื่อความเข้ากันได้แบบย้อนกลับคุณควรโทรหาnext(foo)และใน3.*วิธีการมายากลจะถูกตั้งชื่ออย่างถูกต้อง__next__แทนที่จะเป็น "อ๊ะ - ตา" next! - ).
ดังนั้นกฎทั่วไปไม่ควรเรียกใช้เมธอดมายากลโดยตรง (แต่โดยอ้อมเสมอผ่านบิวท์อิน) เว้นแต่คุณจะรู้แน่ชัดว่าทำไมคุณถึงต้องทำเช่นนั้น (เช่นเมื่อคุณลบล้างเมธอดดังกล่าวในคลาสย่อยหาก คลาสย่อยจำเป็นต้องเลื่อนไปสู่ซูเปอร์คลาสที่ต้องทำผ่านการเรียกอย่างชัดเจนไปยังเมธอดมายากล)
def len(x): return "I am a string." print(len(42)) print(len([1,2,3]))และพิมพ์I am stringสองครั้ง ช่วยอธิบายเพิ่มเติมได้ไหม
__len__พิเศษ(ไม่ใช่ function) บนวัตถุที่กำลังพิจารณา
lenไม่ใช่ฟังก์ชันอื่น ๆ (เช่นในตัวอย่างของฉัน) ที่มีชื่อเดียวกัน - len. ไม่มีคำเตือนเช่น "คุณกำลังกำหนดฟังก์ชัน len ในตัวใหม่" หรืออะไรทำนองนี้ ในความคิดของฉันฉันไม่แน่ใจเกี่ยวกับสิ่งที่อเล็กซ์ระบุไว้ในคำตอบของเขา
len in vars(__builtins__).values().
คุณสามารถคิดว่า len () เทียบเท่ากับ
def len(x):
return x.__len__()
ข้อดีอย่างหนึ่งคือช่วยให้คุณสามารถเขียนสิ่งต่างๆเช่น
somelist = [[1], [2, 3], [4, 5, 6]]
map(len, somelist)
แทน
map(list.__len__, somelist)
หรือ
map(operator.methodcaller('__len__'), somelist)
แม้ว่าจะมีพฤติกรรมที่แตกต่างกันเล็กน้อย ตัวอย่างเช่นในกรณีของ 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.methodcaller operator.attrgetter
คุณสามารถตรวจสอบเอกสาร 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