ฉันกำลังพยายามที่จะเข้าใจเมื่อจะใช้หรือ__getattr__ เอกสารกล่าวถึงนำไปใช้กับการเรียนแบบใหม่ คลาสสไตล์ใหม่คืออะไร __getattribute____getattribute__
ฉันกำลังพยายามที่จะเข้าใจเมื่อจะใช้หรือ__getattr__ เอกสารกล่าวถึงนำไปใช้กับการเรียนแบบใหม่ คลาสสไตล์ใหม่คืออะไร __getattribute____getattribute__
คำตอบ:
ความแตกต่างที่สำคัญระหว่าง__getattr__และ__getattribute__คือ__getattr__เรียกใช้เฉพาะเมื่อแอตทริบิวต์ไม่พบวิธีปกติ เป็นสิ่งที่ดีสำหรับการใช้ทางเลือกสำหรับคุณลักษณะที่ขาดหายไปและอาจเป็นหนึ่งในสองที่คุณต้องการ
__getattribute__ถูกเรียกใช้ก่อนที่จะดูแอตทริบิวต์ที่แท้จริงของวัตถุและอาจมีความยุ่งยากในการใช้อย่างถูกต้อง คุณสามารถจบด้วยการเรียกซ้ำแบบไม่สิ้นสุดได้อย่างง่ายดายมาก
คลาสแบบใหม่ที่ได้รับมาจากคลาสแบบobjectเก่านั้นเป็น Python 2.x โดยไม่มีคลาสพื้นฐานที่ชัดเจน แต่ความแตกต่างระหว่างแบบเก่าและการเรียนแบบใหม่ไม่ได้เป็นหนึ่งที่สำคัญเมื่อมีการเลือกระหว่างและ__getattr____getattribute__
__getattr__คุณเกือบจะแน่นอนต้องการ
__getattribute__จะได้รับการเรียกร้องให้ทุกคนเข้าถึงและ__getattr__จะได้รับการเรียกร้องให้มีครั้งที่ยก__getattribute__ AttributeErrorทำไมไม่เก็บทุกอย่างไว้ในที่เดียว?
__getattribute__แทนที่การโทรไปยังวิธีการที่มีอยู่ที่คุณต้องการจะใช้
objec.__getattribute__เรียกใช้myclass.__getattr__ภายใต้สถานการณ์ที่เหมาะสม
ให้ดูตัวอย่างง่ายๆของวิธีการทั้งสอง__getattr__และ__getattribute__เวทมนตร์
__getattr__Python จะเรียก __getattr__ใช้เมธอดทุกครั้งที่คุณร้องขอแอตทริบิวต์ที่ยังไม่ได้กำหนดไว้ ในตัวอย่างต่อไปนี้การนับชั้นเรียนของฉันไม่มี__getattr__วิธี ตอนนี้เป็นหลักเมื่อฉันพยายามเข้าถึงทั้งสองobj1.myminและobj1.mymaxคุณลักษณะทุกอย่างทำงานได้ดี แต่เมื่อฉันพยายามเข้าถึงobj1.mycurrentattribute - Python ให้ฉันAttributeError: 'Count' object has no attribute 'mycurrent'
class Count():
def __init__(self,mymin,mymax):
self.mymin=mymin
self.mymax=mymax
obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.mycurrent) --> AttributeError: 'Count' object has no attribute 'mycurrent'
ตอนนี้ชั้นเรียนของฉันจำนวนมี__getattr__วิธีการ ตอนนี้เมื่อฉันพยายามเข้าถึง obj1.mycurrentแอ็ตทริบิวต์ - python จะส่งคืนสิ่งที่ฉันได้นำไปใช้ใน__getattr__วิธีการของฉัน ในตัวอย่างของฉันเมื่อใดก็ตามที่ฉันพยายามเรียกแอตทริบิวต์ที่ไม่มีอยู่ python สร้างคุณลักษณะนั้นและตั้งค่าเป็นค่าจำนวนเต็ม 0
class Count:
def __init__(self,mymin,mymax):
self.mymin=mymin
self.mymax=mymax
def __getattr__(self, item):
self.__dict__[item]=0
return 0
obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.mycurrent1)
__getattribute__ตอนนี้ให้ดู__getattribute__วิธีการ ถ้าคุณมี __getattribute__วิธีการสอนในชั้นเรียนของคุณหลามจะเรียกใช้วิธีนี้สำหรับทุก ๆ แอตทริบิวต์ไม่ว่าจะมีอยู่หรือไม่ก็ตาม เหตุใดเราจึงต้องการ__getattribute__วิธีการ เหตุผลหนึ่งที่ดีคือคุณสามารถป้องกันการเข้าถึงคุณลักษณะและทำให้ปลอดภัยยิ่งขึ้นดังที่แสดงในตัวอย่างต่อไปนี้
เมื่อใดก็ตามที่มีคนพยายามเข้าถึงคุณลักษณะของฉันที่เริ่มต้นด้วยpython 'สตริง' substring ทำให้เกิดAttributeErrorข้อยกเว้น มิฉะนั้นจะส่งคืนแอตทริบิวต์นั้น
class Count:
def __init__(self,mymin,mymax):
self.mymin=mymin
self.mymax=mymax
self.current=None
def __getattribute__(self, item):
if item.startswith('cur'):
raise AttributeError
return object.__getattribute__(self,item)
# or you can use ---return super().__getattribute__(item)
obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.current)
สำคัญ: เพื่อหลีกเลี่ยงการเรียกซ้ำแบบไม่สิ้นสุดใน__getattribute__เมธอดการประยุกต์ใช้ควรเรียกเมธอดคลาสพื้นฐานที่มีชื่อเดียวกันเพื่อเข้าถึงแอ็ตทริบิวต์ที่ต้องการ ตัวอย่างเช่น: object.__getattribute__(self, name)หรือ super().__getattribute__(item)ไม่self.__dict__[item]
หากคลาสของคุณมีทั้งวิธี getattrและgetattributeวิธีการแบบเวทย์มนตร์นั้น __getattribute__จะถูกเรียกก่อน แต่ถ้า __getattribute__เพิ่ม
AttributeErrorข้อยกเว้นข้อยกเว้นจะถูกละเว้นและ__getattr__วิธีการจะถูกเรียกใช้ ดูตัวอย่างต่อไปนี้:
class Count(object):
def __init__(self,mymin,mymax):
self.mymin=mymin
self.mymax=mymax
self.current=None
def __getattr__(self, item):
self.__dict__[item]=0
return 0
def __getattribute__(self, item):
if item.startswith('cur'):
raise AttributeError
return object.__getattribute__(self,item)
# or you can use ---return super().__getattribute__(item)
# note this class subclass object
obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.current)
__getattribute__แต่สิ่งนี้ไม่แน่นอน เพราะตามตัวอย่างของคุณสิ่งที่คุณกำลังทำอยู่__getattribute__คือการยกAttributeErrorข้อยกเว้นหากแอตทริบิวต์นั้นไม่มีอยู่ใน__dict__วัตถุ แต่คุณไม่จำเป็นต้องใช้สิ่งนี้เพราะนี่เป็นการใช้งานเริ่มต้น__getattribute__และ infact __getattr__นั้นเป็นสิ่งที่คุณต้องการในฐานะกลไกทางเลือก
currentถูกกำหนดในกรณีของCount(ดู__init__) ดังนั้นเพียงแค่เพิ่มAttributeErrorถ้าแอตทริบิวต์ที่ไม่ได้มีไม่มากสิ่งที่เกิดขึ้น - มันคล้อยตามไป__getattr__สำหรับทุกชื่อขึ้นต้น 'เลว' รวมถึงcurrentแต่ยังcurious, curly...
นี่เป็นเพียงตัวอย่างขึ้นอยู่กับคำอธิบายของเน็ด Batchelder
__getattr__ ตัวอย่าง:
class Foo(object):
def __getattr__(self, attr):
print "looking up", attr
value = 42
self.__dict__[attr] = value
return value
f = Foo()
print f.x
#output >>> looking up x 42
f.x = 3
print f.x
#output >>> 3
print ('__getattr__ sets a default value if undefeined OR __getattr__ to define how to handle attributes that are not found')
และถ้าใช้ตัวอย่างเดียวกันกับ__getattribute__คุณก็จะได้ >>>RuntimeError: maximum recursion depth exceeded while calling a Python object
__getattr__()การใช้งานยอมรับเฉพาะชุด จำกัด ของชื่อแอตทริบิวต์ที่ถูกต้องโดยการเพิ่มAttributeErrorชื่อแอตทริบิวต์ที่ไม่ถูกต้องดังนั้นการหลีกเลี่ยงปัญหาที่ลึกซึ้งและยากต่อการแก้ปัญหา ตัวอย่างนี้โดยไม่มีเงื่อนไขรับทุกชื่อแอตทริบิวต์ที่ถูกต้อง - แปลกประหลาด (และตรงไปตรงมาผิดพลาดได้ง่าย) __getattr__()ใช้ผิดวัตถุประสงค์ของ ถ้าคุณต้องการ "การควบคุมทั้งหมด" มากกว่าการสร้างแอตทริบิวต์ในตัวอย่างนี้คุณต้องการ__getattribute__()แทน
defaultdictมันเป็นเช่นเดียวกับ
__getattr__จะถูกเรียกก่อนการค้นหาซูเปอร์คลาส นี่เป็นเรื่องปกติสำหรับคลาสย่อยโดยตรงของobjectเนื่องจากวิธีการเดียวที่คุณสนใจมีวิธีการเวทย์มนตร์ที่ไม่สนใจอินสแตนซ์ต่อไป แต่สำหรับโครงสร้างการสืบทอดที่ซับซ้อนยิ่งขึ้น
คลาสสไตล์ใหม่สืบทอดจากobjectหรือคลาสสไตล์ใหม่อื่น:
class SomeObject(object):
pass
class SubObject(SomeObject):
pass
ชั้นเรียนแบบเก่าไม่:
class SomeObject:
pass
สิ่งนี้ใช้ได้กับ Python 2 เท่านั้น - ใน Python 3 ทั้งหมดข้างต้นจะสร้างคลาสสไตล์ใหม่
ดูที่9. Classes (Python tutorial), NewClassVsClassicClassและอะไรคือความแตกต่างระหว่างสไตล์เก่ากับคลาสสไตล์ใหม่ใน Python? สำหรับรายละเอียด
คลาสสไตล์ใหม่เป็นคลาสย่อยที่ "object" (โดยตรงหรือโดยอ้อม) พวกเขามี__new__วิธีการเรียนเพิ่มเติม__init__และมีพฤติกรรมระดับต่ำเหตุผลมากกว่า
โดยปกติแล้วคุณจะต้องการแทนที่__getattr__(ถ้าคุณกำลังเอาชนะ) ไม่เช่นนั้นคุณจะมีปัญหาในการรองรับไวยากรณ์ "self.foo" ภายในวิธีการของคุณ
ข้อมูลเพิ่มเติม: http://www.devx.com/opensource/Article/31482/0/page/4
ในการอ่านผ่าน Beazley & Jones PCB ฉันได้สะดุดกับกรณีการใช้งานที่ชัดเจนและใช้งานได้จริงเพื่อ__getattr__ช่วยตอบ "ตอน" ส่วนหนึ่งของคำถามของ OP จากหนังสือ:
" __getattr__()วิธีการดังกล่าวเป็นสิ่งที่จับได้สำหรับการค้นหาแอททริบิวต์มันเป็นวิธีการที่เรียกว่าหากรหัสพยายามเข้าถึงแอททริบิวที่ไม่มีอยู่จริง" เรารู้เรื่องนี้จากคำตอบข้างต้น แต่ในสูตร PCB 8.15, ฟังก์ชั่นนี้จะใช้ในการใช้รูปแบบการออกแบบคณะผู้แทน ถ้า Object A มีคุณสมบัติ Object B ที่ใช้วิธีการหลายอย่างที่ Object A ต้องการมอบหมายแทนการกำหนดวิธีการทั้งหมดของ Object B ใน Object A ใหม่เพื่อเรียกเมธอดของ Object B ให้นิยาม__getattr__()วิธีดังนี้:
def __getattr__(self, name):
return getattr(self._b, name)
โดยที่ _b เป็นชื่อของแอททริบิวของ Object A ที่เป็น Object B เมื่อเมธอดที่กำหนดไว้บน Object B ถูกเรียกบน Object A __getattr__เมธอดจะถูกเรียกใช้ที่ส่วนท้ายของเชนการค้นหา สิ่งนี้จะทำให้โค้ดสะอาดขึ้นเช่นกันเนื่องจากคุณไม่มีรายการวิธีที่กำหนดไว้สำหรับการมอบหมายให้วัตถุอื่นเท่านั้น