__Getattr__ बनाम __getattribute__ के बीच अंतर


417

मैं समझने की कोशिश कर रहा हूं कि कब उपयोग करना है __getattr__या नहीं __getattribute__प्रलेखन का उल्लेख है __getattribute__नई शैली वर्गों के लिए लागू होता है। नई शैली की कक्षाएं क्या हैं?



5
@ प्रश्न अभी तक उस प्रश्न का उत्तर यहाँ से दिया गया है ...
JC Rocemonte

जवाबों:


497

के बीच एक महत्वपूर्ण अंतर यह है __getattr__और __getattribute__वह यह है कि __getattr__केवल अगर विशेषता सामान्य तरीकों से नहीं मिला था शुरू हो जाती है। यह लापता विशेषताओं के लिए एक वापसी को लागू करने के लिए अच्छा है, और शायद दो में से एक है जिसे आप चाहते हैं।

__getattribute__ऑब्जेक्ट पर वास्तविक विशेषताओं को देखने से पहले आह्वान किया जाता है, और इसलिए इसे सही ढंग से लागू करने के लिए मुश्किल हो सकता है। आप अनंत पुनरावृत्तियों में बहुत आसानी से समाप्त हो सकते हैं।

नई शैली की कक्षाएं object, पुरानी शैली की कक्षाओं से प्राप्त होती हैं , जो कि बिना किसी स्पष्ट आधार वर्ग के पायथन 2.x में हैं। लेकिन पुरानी शैली और नई शैली की कक्षाओं के बीच का अंतर महत्वपूर्ण नहीं है, जब आपस में चुनते हैं __getattr__और __getattribute__

आप लगभग निश्चित रूप से चाहते हैं __getattr__


6
क्या मैं दोनों को एक ही कक्षा में लागू कर सकता हूँ? अगर मैं कर सकता हूं, तो दोनों को लागू करने के लिए क्या है?
अल्कोट

13
@ अलॉट: आप उन दोनों को लागू कर सकते हैं, लेकिन मुझे यकीन नहीं है कि आप क्यों करेंगे। __getattribute__हर पहुंच के लिए बुलाया जाएगा, और __getattr__उस समय के लिए कॉल किया जाएगा जिसने __getattribute__उठाया AttributeError। सिर्फ एक में ही क्यों न रखें?
नेड बैचेल्ड

10
@ NedBatchelder, यदि आप (सशर्त रूप से) मौजूदा विधियों में कॉल को ओवरराइड करना चाहते हैं, तो आप उपयोग करना चाहेंगे __getattribute__
जैस ब्राउनिंग

28
"इस विधि में अनंत पुनरावृत्ति से बचने के लिए, इसके कार्यान्वयन को हमेशा आधार वर्ग विधि को उसी नाम के साथ कॉल करना चाहिए, जिसमें किसी भी विशेषता की आवश्यकता होती है, उदाहरण के लिए, ऑब्जेक्ट ।__ getattribute __ (स्व, नाम)।"
किमी।

1
@kmonsoor। दोनों को लागू करने का यह एक अच्छा कारण है। सही परिस्थितियों में objec.__getattribute__आक्रमण myclass.__getattr__
मैड फिजिसिस्ट

138

आओ हम दोनों __getattr__और __getattribute__जादू विधियों के कुछ सरल उदाहरण देखें ।

__getattr__

__getattr__जब भी आप किसी विशेषता का अनुरोध करते हैं, जिसे आप पहले से परिभाषित नहीं करते हैं, तो पायथन विधि को कॉल करेगा । निम्नलिखित उदाहरण में मेरे वर्ग गणना की कोई __getattr__विधि नहीं है। अब मुख्य में जब मैं दोनों का उपयोग करने की कोशिश करता हूं obj1.myminऔर obj1.mymaxसब कुछ ठीक काम करता है। लेकिन जब मैं obj1.mycurrentविशेषता तक पहुंचने की कोशिश करता हूं - पायथन मुझे देता है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विशेषता तक पहुंचने की कोशिश करता हूं - अजगर ने मुझे मेरे __getattr__तरीके से जो भी लागू किया है वह मुझे वापस कर देता है । मेरे उदाहरण में जब भी मैं एक विशेषता को कॉल करने की कोशिश करता हूं जो मौजूद नहीं है, तो अजगर उस विशेषता को बनाता है और इसे पूर्णांक मान 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__विधि की आवश्यकता क्यों है ? एक अच्छा कारण यह है कि आप विशेषताओं तक पहुंच को रोक सकते हैं और उन्हें अधिक सुरक्षित बना सकते हैं जैसा कि निम्नलिखित उदाहरण में दिखाया गया है।

जब भी कोई मेरी विशेषताओं को एक्सेस करने की कोशिश करता है, जो कि 'कर्व' अजगर के विकल्प से शुरू होता है, 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]

जरूरी

यदि आपकी कक्षा में गेटअटर और गेटअट्रीब्यूट मैजिक दोनों विधियाँ हैं तो __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)

1
मुझे यकीन नहीं है कि ओवरराइडिंग के लिए वास्तव में क्या उपयोग होगा, __getattribute__लेकिन निश्चित रूप से यह नहीं है। क्योंकि आपके उदाहरण प्रति, तुम सब में क्या कर रहे हैं __getattribute__ला रहा है AttributeErrorअपवाद अगर विशेषता में वहाँ नहीं है __dict__वस्तु की; लेकिन आपको वास्तव में इसकी आवश्यकता नहीं है क्योंकि यह डिफ़ॉल्ट कार्यान्वयन है __getattribute__और वास्तव __getattr__में वही है जो आपको एक कमबैक तंत्र के रूप में चाहिए।
रोहित

@Rohit currentके उदाहरण पर परिभाषित किया गया है Count(देखें __init__), तो बस ऊपर उठाने AttributeErrorविशेषता वहाँ नहीं है है, तो काफी क्या हो रहा है नहीं है - यह को defers __getattr__के लिए सभी नाम 'CUR' शुरू करने, सहित current, लेकिन यह भी curious, curly...
joelb

16

यह नेड बैचेलेडर की व्याख्या पर आधारित एक उदाहरण मात्र है

__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


9
वास्तव में, यह भयानक है। वास्तविक-विश्व __getattr__()कार्यान्वयन केवल AttributeErrorअमान्य विशेषता नामों के लिए वैध विशेषता नामों का एक निश्चित सेट स्वीकार करते हैं , जिससे सूक्ष्म और कठिन-से-डिबग मुद्दों से बचते हैं । यह उदाहरण बिना शर्त के सभी विशेषता नामों को मान्य मानता है - एक विचित्र (और स्पष्ट रूप से त्रुटि-प्रवण) दुरुपयोग __getattr__()। यदि आप इस उदाहरण में विशेषता निर्माण पर "कुल नियंत्रण" चाहते हैं, तो आप __getattribute__()इसके बजाय चाहते हैं।
सेसिल करी

3
@CecilCurry: वे सभी मुद्दे जिनसे आप जुड़े हुए हैं, इसमें निहित है कि मूल्य के बजाय कोई नहीं लौटाता है, जो यह उत्तर नहीं देता है। सभी विशेषता नामों को स्वीकार करने में क्या गलत है? यह एक के रूप में ही है defaultdict
निक मैटेओ

2
समस्या यह है कि सुपरक्लास लुकअप से पहले__getattr__ कॉल किया जाएगा । यह एक प्रत्यक्ष उपवर्ग के लिए ठीक है , क्योंकि केवल वही विधियाँ हैं जिनके बारे में आप वास्तव में परवाह करते हैं जादू की विधियाँ हैं जो वैसे भी उदाहरण को अनदेखा करते हैं, लेकिन किसी भी अधिक जटिल विरासत संरचना के लिए, आप पूरी तरह से माता-पिता से कुछ भी प्राप्त करने की क्षमता को हटा देते हैं। object
मैड फिजिसिस्ट

1
@Simon K Bhatta4ya, आपका अंतिम प्रिंट स्टेटमेंट एक टिप्पणी है। सही? यह पढ़ने के लिए एक लंबी लाइन और थकाऊ है (किसी को दाईं ओर बहुत स्क्रॉल करना है)। कोड सेक्शन के बाद लाइन लगाने के बारे में क्या? या यदि आप इसे कोड सेक्शन में रखना चाहते हैं, तो मुझे लगता है कि इसे दो अलग-अलग लाइनों में तोड़ना बेहतर होगा।
एमडी अबू नफी इब्ना जाहिद

14

नई शैली की कक्षाएं object, या किसी अन्य नई शैली वर्ग से विरासत में मिली हैं :

class SomeObject(object):
    pass

class SubObject(SomeObject):
    pass

पुरानी शैली की कक्षाएं नहीं:

class SomeObject:
    pass

यह केवल पायथन 2 पर लागू होता है - पायथन 3 में उपरोक्त सभी नई शैली की कक्षाएं बनाएंगे।

देखें 9. कक्षाएं (पायथन ट्यूटोरियल), न्यूक्लास वीक्स्लैकक्लास और पायथन में पुरानी शैली और नई शैली वर्गों के बीच क्या अंतर है? ब्योरा हेतु।


6

नई शैली की कक्षाएं वे हैं जो "वस्तु" (सीधे या अप्रत्यक्ष रूप से) को उपवर्गित करती हैं। उनके पास __new__इसके अलावा एक वर्ग विधि है__init__ और कुछ अधिक तर्कसंगत निम्न-स्तरीय व्यवहार है।

आमतौर पर, आप ओवरराइड करना चाहेंगे __getattr__ (यदि आप या तो ओवरराइड कर रहे हैं), अन्यथा आपके पास अपने तरीकों के भीतर "सेल्फू" सिंटैक्स का समर्थन करने का कठिन समय होगा।

अतिरिक्त जानकारी: http://www.devx.com/opensource/Article/31482/0/page/4


2

बेज़ले और जोन्स पीसीबी के माध्यम से पढ़ने में, मैं एक स्पष्ट और व्यावहारिक उपयोग के मामले पर ठोकर खाई है __getattr__ है जो ओपी के प्रश्न के "जब" भाग का जवाब देने में मदद करता है। पुस्तक से:

" __getattr__()विधि विशेषता लुकअप के लिए कैच-ऑल की तरह है। यह एक विधि है जिसे कॉल किया जाता है यदि कोड मौजूद विशेषता को एक्सेस करने का प्रयास करता है।" हम इसे उपरोक्त उत्तरों से जानते हैं, लेकिन पीसीबी नुस्खा 8.15 में, इस कार्यक्षमता का उपयोग डेलिगेशन डिज़ाइन पैटर्न को लागू करने के लिए किया जाता है । यदि ऑब्जेक्ट ए में एक विशेषता ऑब्जेक्ट बी है जो ऑब्जेक्ट ए में ऑब्जेक्ट बी के सभी तरीकों को फिर से परिभाषित करने के बजाय ऑब्जेक्ट ए को प्रतिनिधि बनाना चाहता है, तो ऑब्जेक्ट बी के तरीकों को कॉल करने के लिए केवल __getattr__()निम्न तरीकों को परिभाषित करें :

def __getattr__(self, name):
    return getattr(self._b, name)

जहाँ _b ऑब्जेक्ट A की विशेषता का नाम है, जो कि ऑब्जेक्ट B है। जब ऑब्जेक्ट B पर परिभाषित विधि को ऑब्जेक्ट A पर बुलाया जाता है, तो __getattr__विधि लुकअप चेन के अंत में आ जाएगी। यह कोड क्लीनर भी बना देगा, क्योंकि आपके पास किसी अन्य वस्तु को प्रस्तुत करने के लिए परिभाषित तरीकों की सूची नहीं है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.