ทำความเข้าใจความแตกต่างระหว่าง __getattr__ และ __getattribute__


206

ฉันพยายามที่จะเข้าใจความแตกต่างระหว่าง__getattr__และ__getattribute__อย่างไรก็ตามฉันล้มเหลว

คำตอบของคำถามกองมากเกินความแตกต่างระหว่าง__getattr__VS__getattribute__พูดว่า:

__getattribute__ถูกเรียกใช้ก่อนที่จะดูแอตทริบิวต์ที่แท้จริงของวัตถุและอาจมีความยุ่งยากในการใช้อย่างถูกต้อง คุณสามารถจบด้วยการเรียกซ้ำแบบไม่สิ้นสุดได้อย่างง่ายดายมาก

ฉันไม่รู้ว่ามันแปลว่าอะไร

จากนั้นจะกล่าวต่อไปว่า:

__getattr__คุณเกือบจะแน่นอนต้องการ

ทำไม?

ฉันอ่านว่าถ้า__getattribute__ล้มเหลว__getattr__ถูกเรียก เหตุใดจึงมีสองวิธีที่แตกต่างกันในการทำสิ่งเดียวกัน หากรหัสของฉันใช้คลาสสไตล์ใหม่ฉันควรใช้อะไร

ฉันกำลังมองหาตัวอย่างโค้ดเพื่อล้างคำถามนี้ ฉันใช้ Google อย่างดีที่สุดเพื่อความสามารถของฉัน แต่คำตอบที่ฉันพบไม่ได้พูดถึงปัญหาอย่างถี่ถ้วน

หากมีเอกสารใด ๆ ฉันพร้อมที่จะอ่าน


2
ถ้ามันช่วยได้จากเอกสาร "เพื่อหลีกเลี่ยงการเรียกซ้ำไม่สิ้นสุดในวิธีนี้การใช้งานนั้นควรเรียกเมธอดคลาสพื้นฐานที่มีชื่อเดียวกันเพื่อเข้าถึงแอททริบิวใด ๆ ที่มันต้องการเช่น object .__ getattribute __ (ตัวเองชื่อ) " [ docs.python.org/2/reference/ ......
kmonsoor

คำตอบ:


306

พื้นฐานบางอย่างก่อน

ด้วยวัตถุคุณต้องจัดการกับคุณลักษณะของมัน instance.attributeปกติที่เราทำ บางครั้งเราต้องการการควบคุมมากขึ้น (เมื่อเราไม่ทราบชื่อของแอตทริบิวต์ล่วงหน้า)

ยกตัวอย่างเช่นจะกลายเป็นinstance.attribute getattr(instance, attribute_name)เมื่อใช้โมเดลนี้เราสามารถรับแอตทริบิวต์ได้โดยระบุ attribute_nameเป็นสตริง

การใช้ __getattr__

นอกจากนี้คุณยังสามารถบอกชั้นเรียนถึงวิธีจัดการกับคุณลักษณะที่ไม่ได้จัดการอย่างชัดเจนและทำได้โดย__getattr__ใช้วิธีการ

Python จะเรียกวิธีนี้เมื่อใดก็ตามที่คุณร้องขอคุณลักษณะที่ยังไม่ได้กำหนดไว้ดังนั้นคุณสามารถกำหนดว่าจะทำอย่างไรกับมัน

กรณีการใช้งานแบบคลาสสิก:

class A(dict):
    def __getattr__(self, name):
       return self[name]
a = A()
# Now a.somekey will give a['somekey']

คำเตือนและการใช้ __getattribute__

หากคุณต้องการจับทุกแอตทริบิวต์โดยไม่คำนึงว่ามีอยู่หรือไม่ให้ใช้__getattribute__แทน ความแตกต่างคือการ__getattr__ได้รับเรียกเท่านั้นสำหรับคุณลักษณะที่ไม่มีอยู่จริง __getattr__ถ้าคุณตั้งค่าแอตทริบิวต์โดยตรงอ้างอิงแอตทริบิวต์ที่จะดึงมันโดยไม่ต้องโทร

__getattribute__ เรียกว่าตลอดเวลา


"ตัวอย่างเช่น instance.attribute จะกลายเป็นgetattr(instance, attribute_name)". ไม่ควร__getattribute__(instance, attribute_name)หรือ
Md. Abu Nafee Ibna Zahid

1
@ Md.AbuNafeeIbnaZahid getattrเป็นฟังก์ชันในตัว เทียบเท่ากับgetattr(foo, 'bar') foo.bar
wizzwizz4

น่าสังเกตว่า__getattr__จะได้รับการเรียกเท่านั้นถ้ามันถูกกำหนดเพราะมันไม่ได้รับมาจากobject
joelb

94

__getattribute__ ถูกเรียกเมื่อใดก็ตามที่มีการเข้าถึงคุณลักษณะ

class Foo(object):
    def __init__(self, a):
        self.a = 1

    def __getattribute__(self, attr):
        try:
            return self.__dict__[attr]
        except KeyError:
            return 'default'
f = Foo(1)
f.a

สิ่งนี้จะทำให้เกิดการเรียกซ้ำแบบไม่สิ้นสุด return self.__dict__[attr]ผู้ร้ายที่นี่เป็นเส้น มาทำเป็นแกล้งกัน (มันใกล้ความจริงมากพอ) ว่ามีการเก็บแอตทริบิวต์ทั้งหมดไว้ในself.__dict__ชื่อของพวกเขา เส้น

f.a

ความพยายามที่จะเข้าถึงแอตทริบิวต์ของa fสายf.__getattribute__('a')นี้ แล้วพยายามที่จะโหลด__getattribute__ เป็นแอททริบิวต์ดังนั้น python จึงเรียกอีกครั้งว่าพยายามเข้าถึงแอททริบิว' นี่เป็นการเรียกซ้ำแบบไม่สิ้นสุดself.__dict____dict__self == ff.__getattribute__('__dict__')'__dict__

หาก__getattr__เคยถูกใช้มาแล้ว

  1. มันจะไม่ทำงานเพราะfมีaคุณสมบัติ
  2. ถ้ามันวิ่ง (ขอบอกว่าคุณถามf.b) แล้วมันจะไม่ได้รับการเรียกตัวไปพบ__dict__เพราะมันมีอยู่แล้วและ__getattr__มีการเรียกใช้เฉพาะในกรณีที่วิธีการอื่น ๆ ในการหาแอตทริบิวต์ได้ล้มเหลว

วิธี 'ถูกต้อง' ในการเขียนคลาสข้างต้นโดยใช้__getattribute__คือ

class Foo(object):
    # Same __init__

    def __getattribute__(self, attr):
        return super(Foo, self).__getattribute__(attr)

super(Foo, self).__getattribute__(attr)ผูก__getattribute__เมธอดของซุปเปอร์คลาสที่ใกล้ที่สุด (อย่างเป็นทางการคลาสถัดไปในระเบียบวิธีการแก้ปัญหาของคลาสหรือ MRO) ไปยังวัตถุปัจจุบันselfจากนั้นเรียกมันและอนุญาตให้ทำงานได้

ปัญหาทั้งหมดนี้หลีกเลี่ยงได้โดยการใช้__getattr__ซึ่งทำให้ Python ทำสิ่งนั้นเป็นเรื่องปกติจนกระทั่งไม่พบคุณลักษณะ ณ จุดนั้น Python มือควบคุม__getattr__วิธีการของคุณและช่วยให้มันมาพร้อมกับบางสิ่งบางอย่าง

นอกจากนี้ยังเป็นที่น่าสังเกตว่าคุณสามารถใช้เป็น infinite __getattr__เรียกซ้ำกับ

class Foo(object):
    def __getattr__(self, attr):
        return self.attr

ฉันจะปล่อยให้สิ่งนั้นเป็นการออกกำลังกาย


60

ผมคิดว่าคำตอบอื่น ๆ ได้ทำผลงานยอดเยี่ยมของการอธิบายความแตกต่างระหว่าง__getattr__และแต่สิ่งหนึ่งที่อาจจะไม่ชัดเจนคือเหตุผลที่คุณต้องการจะใช้__getattribute__ __getattribute__สิ่งที่ยอดเยี่ยมเกี่ยวกับ__getattribute__ว่ามันช่วยให้คุณเกินจุดเมื่อเข้าถึงชั้นเรียน สิ่งนี้ช่วยให้คุณกำหนดวิธีการเข้าถึงแอตทริบิวต์ในระดับต่ำ ตัวอย่างเช่นสมมติว่าฉันต้องการกำหนดคลาสที่เมธอดทั้งหมดที่รับอาร์กิวเมนต์ตัวเองเท่านั้นถือว่าเป็นคุณสมบัติ:

# prop.py
import inspect

class PropClass(object):
    def __getattribute__(self, attr):
        val = super(PropClass, self).__getattribute__(attr)
        if callable(val):
            argcount = len(inspect.getargspec(val).args)
            # Account for self
            if argcount == 1:
                return val()
            else:
                return val
        else:
            return val

และจากล่ามแบบโต้ตอบ:

>>> import prop
>>> class A(prop.PropClass):
...     def f(self):
...             return 1
... 
>>> a = A()
>>> a.f
1

หลักสูตรนี้เป็นตัวอย่างที่โง่และคุณอาจจะไม่เคยต้องการที่จะทำแบบนี้ __getattribute__แต่มันแสดงให้เห็นคุณอำนาจที่คุณจะได้รับจากการเอาชนะ


6

ฉันได้ผ่านการอธิบายที่ยอดเยี่ยมของผู้อื่น แต่ผมพบคำตอบง่ายๆจากบล็อกนี้หลามวิธีเมจิกและ __getattr__ทั้งหมดต่อไปนี้มาจากที่นั่น

ด้วย__getattr__วิธีการทางเวทย์มนตร์เราสามารถสกัดกั้นการค้นหาแอ็ตทริบิวต์ที่ไม่มีอยู่และทำบางสิ่งเพื่อให้มันไม่ล้มเหลว:

class Dummy(object):

    def __getattr__(self, attr):
        return attr.upper()

d = Dummy()
d.does_not_exist # 'DOES_NOT_EXIST'
d.what_about_this_one  # 'WHAT_ABOUT_THIS_ONE'

แต่ถ้ามีแอตทริบิวต์อยู่__getattr__จะไม่ถูกเรียกใช้:

class Dummy(object):

    def __getattr__(self, attr):
        return attr.upper()

d = Dummy()
d.value = "Python"
print(d.value)  # "Python"

__getattribute__คล้าย__getattr__กับด้วยความแตกต่างที่สำคัญที่__getattribute__จะขัดขวางการค้นหาแอตทริบิวต์EVERYไม่ว่าจะมีแอตทริบิวต์นั้นอยู่หรือไม่ก็ตาม

class Dummy(object):

    def __getattribute__(self, attr):
        return 'YOU SEE ME?'

d = Dummy()
d.value = "Python"
print(d.value)  # "YOU SEE ME?"

ในตัวอย่างนั้นdวัตถุนั้นมีค่าของคุณลักษณะอยู่แล้ว แต่เมื่อเราพยายามเข้าถึงเราจะไม่ได้รับค่าที่คาดหวังดั้งเดิม (“ Python”); เราแค่รับสิ่งที่__getattribute__คืนมา หมายความว่าเราได้สูญเสียคุณลักษณะมูลค่า มันกลายเป็น“ ไม่สามารถเข้าถึงได้”

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