ฉันจะแทนที่ __getattr__ ใน Python โดยไม่ทำลายพฤติกรรมเริ่มต้นได้อย่างไร


185

ฉันต้องการแทนที่__getattr__วิธีการในชั้นเรียนเพื่อทำสิ่งที่แฟนซี แต่ฉันไม่ต้องการที่จะทำลายพฤติกรรมเริ่มต้น

วิธีที่ถูกต้องในการทำเช่นนี้คืออะไร?


20
"แตก" มันถามไม่ใช่ "เปลี่ยน" มีความชัดเจนเพียงพอ: แอตทริบิวต์ "แฟนซี" ไม่ควรยุ่งเกี่ยวกับแอตทริบิวต์ในตัวและควรประพฤติตนให้มากที่สุดเท่าที่จะเป็นไปได้ คำตอบของ Michael นั้นถูกต้องและเป็นประโยชน์
olooney

คำตอบ:


268

การเอาชนะ__getattr__ควรจะดี - __getattr__ถูกเรียกว่าเป็นทางเลือกสุดท้ายเช่นหากไม่มีแอตทริบิวต์ในอินสแตนซ์ที่ตรงกับชื่อ ตัวอย่างเช่นถ้าคุณเข้าถึงfoo.barแล้ว__getattr__จะถูกเรียกว่าถ้ามีแอตทริบิวต์ไม่มีที่เรียกว่าfoo barหากแอตทริบิวต์นั้นเป็นคุณสมบัติที่คุณไม่ต้องการจัดการให้เพิ่มAttributeError:

class Foo(object):
    def __getattr__(self, name):
        if some_predicate(name):
            # ...
        else:
            # Default behaviour
            raise AttributeError

อย่างไรก็ตามไม่เหมือน__getattr__กัน__getattribute__จะถูกเรียกก่อน (ใช้ได้กับคลาสสไตล์ใหม่เท่านั้นเช่นคลาสที่สืบทอดจากออบเจ็กต์) ในกรณีนี้คุณสามารถรักษาพฤติกรรมเริ่มต้นเช่น:

class Foo(object):
    def __getattribute__(self, name):
        if some_predicate(name):
            # ...
        else:
            # Default behaviour
            return object.__getattribute__(self, name)

ดูเอกสารงูใหญ่มาก


อืมการแก้ไขของคุณมีสิ่งเดียวกับที่ฉันแสดงในคำตอบของฉัน +1

12
เจ๋งมาก Python ไม่ชอบเรียก super เข้ามา__getattr__- มีความคิดอะไรที่ต้องทำบ้าง? ( AttributeError: 'super' object has no attribute '__getattr__')
gatoatigrado

1
โดยไม่เห็นรหัสของคุณมันเป็นเรื่องยากที่จะบอก แต่รูปลักษณ์ที่เหมือนใครของ superclasses ของคุณกำหนดgetattr
โคลิน vH

สิ่งนี้ใช้ได้กับ Hasattr ด้วยเพราะ: "สิ่งนี้ถูกนำไปใช้โดยการเรียก getattr (วัตถุชื่อ) และดูว่ามันทำให้เกิดข้อยกเว้นหรือไม่" docs.python.org/2/library/functions.html#hasattr
ShaBANG

1
-1 สิ่งนี้จะแก้ไขพฤติกรรมเริ่มต้น ตอนนี้คุณมีการAttributeErrorไม่มีบริบทของคุณลักษณะในข้อยกเว้นที่เกิดขึ้น
วิมล

34
class A(object):
    def __init__(self):
        self.a = 42

    def __getattr__(self, attr):
        if attr in ["b", "c"]:
            return 42
        raise AttributeError("%r object has no attribute %r" %
                             (self.__class__.__name__, attr))

>>> a = A()
>>> a.a
42
>>> a.b
42
>>> a.missing
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in __getattr__
AttributeError: 'A' object has no attribute 'missing'
>>> hasattr(a, "b")
True
>>> hasattr(a, "missing")
False

ขอบคุณสำหรับสิ่งนี้. แค่ต้องการให้แน่ใจว่าฉันมีข้อความเริ่มต้นที่ถูกต้องโดยไม่ต้องขุดในแหล่งที่มา
ShawnFumo

4
ฉันคิดว่าself.__class__.__name__ควรใช้แทนself.__class__ในกรณีที่มีการแทนที่ชั้นเรียน__repr__
Michael Scott Cuthbert

3
นี่เป็นคำตอบที่ดีกว่าคำตอบที่ยอมรับ แต่มันก็ดีที่จะไม่ต้องเขียนรหัสใหม่และอาจพลาดการเปลี่ยนแปลงต้นน้ำหากถ้อยคำนั้นถูกแก้ไขหรือเพิ่มบริบทเพิ่มเติมให้กับวัตถุข้อยกเว้นในอนาคต
Wim

13

หากต้องการขยายคำตอบของ Michael หากคุณต้องการรักษาการใช้งานที่เป็นค่าเริ่มต้น__getattr__คุณสามารถทำได้ดังนี้:

class Foo(object):
    def __getattr__(self, name):
        if name == 'something':
            return 42

        # Default behaviour
        return self.__getattribute__(name)

ตอนนี้ข้อความแสดงข้อยกเว้นมีความหมายมากขึ้น:

>>> foo.something
42
>>> foo.error
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in __getattr__
AttributeError: 'Foo' object has no attribute 'error'

2
@ fed.pavlo คุณแน่ใจหรือไม่? บางทีคุณอาจผสม__getattr__และ__getattribute__?
José Luis

ความผิดฉันเอง. ฉันไม่ได้รับสายจากวิธีการอื่นด้วยตนเอง ; (
fed.pavlo

2
แน่นอน @ คำตอบของ Michael ไม่สมบูรณ์หากไม่มีคำตอบนี้
Grijesh Chauhan
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.