ฉันกำลังพยายามที่จะเข้าใจเมื่อจะใช้หรือ__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.mycurrent
attribute - 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__
เมธอดจะถูกเรียกใช้ที่ส่วนท้ายของเชนการค้นหา สิ่งนี้จะทำให้โค้ดสะอาดขึ้นเช่นกันเนื่องจากคุณไม่มีรายการวิธีที่กำหนดไว้สำหรับการมอบหมายให้วัตถุอื่นเท่านั้น