मैं अनंत पुनरावृत्ति त्रुटि के बिना __getattribute__ को कैसे लागू कर सकता हूं?


100

मैं एक वर्ग में एक चर तक पहुंच को ओवरराइड करना चाहता हूं, लेकिन अन्य सभी को सामान्य रूप से वापस कर देता हूं। मैं इसे कैसे पूरा करूं?__getattribute__ ?

मैंने निम्नलिखित की कोशिश की (जिसमें यह भी वर्णन करना चाहिए कि मैं क्या करने की कोशिश कर रहा हूं), लेकिन मुझे एक पुनरावृत्ति त्रुटि मिलती है:

class D(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self,name):
        if name=='test':
            return 0.
        else:
            return self.__dict__[name]

>>> print D().test
0.0
>>> print D().test2
...
RuntimeError: maximum recursion depth exceeded in cmp

जवाबों:


126

आपको एक पुनरावर्ती त्रुटि मिलती है क्योंकि आपके self.__dict__अंदर की विशेषता तक पहुंचने का आपका प्रयास __getattribute__आपके __getattribute__फिर से आह्वान करता है । यदि आप का उपयोग objectहै ' __getattribute__इसके बजाय, यह काम करता है:

class D(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self,name):
        if name=='test':
            return 0.
        else:
            return object.__getattribute__(self, name)

यह काम करता है क्योंकि object(इस उदाहरण में) आधार वर्ग है। __getattribute__आप के आधार संस्करण को कॉल करके पहले आप जिस पुनरावर्ती नरक से बच रहे थे।

Ioothon foo.py में कोड के साथ आउटपुट:

In [1]: from foo import *

In [2]: d = D()

In [3]: d.test
Out[3]: 0.0

In [4]: d.test2
Out[4]: 21

अपडेट करें:

वर्तमान प्रलेखन में नई शैली की कक्षाओं के लिए अधिक विशेषता पहुंच शीर्षक वाले अनुभाग में कुछ है , जहां वे अनंत पुनरावृत्ति से बचने के लिए ऐसा करने की सलाह देते हैं।


1
दिलचस्प। तो आप वहाँ क्या कर रहे हैं? ऑब्जेक्ट को मेरे चर क्यों होंगे?
ग्रेग

1
..और क्या मैं हमेशा ऑब्जेक्ट का उपयोग करना चाहता हूं? यदि मुझे अन्य वर्गों से विरासत में मिली तो क्या होगा?
ग्रेग

1
हां, हर बार जब आप एक वर्ग बनाते हैं और आप अपना स्वयं का लेखन नहीं करते हैं, तो आप ऑब्जेक्ट द्वारा आपूर्ति की जाने वाली गेटअटर का उपयोग करते हैं ।
Egil

19
क्या सुपर का उपयोग करना बेहतर नहीं है () और इस तरह अपने बेस क्लासेस में पहले मिलने वाली विधि अजगर का उपयोग करें ? -super(D, self).__getattribute__(name)
गेपटिनो 13

10
या आप सिर्फ super().__getattribute__(name)Python 3.
jeromej

25

दरअसल, मेरा मानना ​​है कि आप __getattr__इसके बजाय विशेष विधि का उपयोग करना चाहते हैं ।

पायथन डॉक्स से उद्धरण:

__getattr__( self, name)

कॉल किया जाता है जब एक विशेषता लुकअप को सामान्य स्थानों में विशेषता नहीं मिली है (यानी यह एक उदाहरण विशेषता नहीं है और न ही यह स्वयं के लिए क्लास ट्री में पाया गया है)। नाम विशेषता नाम है। इस विधि को (गणना) विशेषता मान वापस करना चाहिए या एक एट्रीब्यूटर अपवाद उठाना चाहिए।
ध्यान दें कि यदि विशेषता सामान्य तंत्र के माध्यम से पाई जाती है, तो __getattr__()इसे नहीं कहा जाता है। (यह के बीच एक जानबूझकर विषमता है __getattr__()और __setattr__()यह दक्षता कारणों के लिए दोनों से किया जाता है और क्योंकि अन्यथा।) __setattr__()उदाहरण की अन्य विशेषताओं का उपयोग करने के लिए कोई रास्ता नहीं होगा। ध्यान दें कि कम से कम उदाहरण के चर के लिए, आप उदाहरण विशेषता शब्दकोश में कोई मान न डालकर (लेकिन किसी अन्य ऑब्जेक्ट में डालने के बजाय) कुल नियंत्रण नकली कर सकते हैं। देखें__getattribute__() नई शैली की कक्षाओं में वास्तव में कुल नियंत्रण प्राप्त करने के तरीके के लिए नीचे दी गई विधि।

नोट: इसके लिए काम करने के लिए, उदाहरण में विशेषता नहीं होनी चाहिए test, इसलिए लाइन self.test=20को हटा दिया जाना चाहिए।


2
वास्तव में, ओपी के कोड की प्रकृति के अनुसार, अधिभावी __getattr__के लिए testबेकार, होगा क्योंकि यह हमेशा यह "सामान्य स्थानों में" पाते हैं।
केसी कुबैल

1
प्रासंगिक पायथन डॉक्स की वर्तमान लिंक (उत्तर में उस संदर्भ से भिन्न प्रतीत होती है): docs.python.org/3/reference/datamodel.html#object.__getattr__
क्रेपसेट

17

पायथन भाषा संदर्भ:

इस पद्धति में अनंत पुनरावृत्ति से बचने के लिए, इसके कार्यान्वयन को हमेशा आधार वर्ग विधि को उसी नाम से पुकारना चाहिए, जिसे किसी भी विशेषता की आवश्यकता होती है, उदाहरण के लिए object.__getattribute__(self, name)

अर्थ:

def __getattribute__(self,name):
    ...
        return self.__dict__[name]

आप एक विशेषता के लिए बुला रहे हैं __dict__। क्योंकि यह एक विशेषता है, जिसे कॉल के __getattribute__लिए खोज में बुलाया जाता है , __dict__जो कॉल __getattribute__करता है ... यदा यदा यदा

return  object.__getattribute__(self, name)

बेस कक्षाओं का उपयोग __getattribute__वास्तविक विशेषता खोजने में मदद करता है।


13

क्या आप वाकई उपयोग करना चाहते हैं __getattribute__? आप वास्तव में क्या हासिल करने की कोशिश कर रहे हैं?

सबसे आसान तरीका है कि आप क्या पूछें:

class D(object):
    def __init__(self):
        self.test = 20
        self.test2 = 21

    test = 0

या:

class D(object):
    def __init__(self):
        self.test = 20
        self.test2 = 21

    @property
    def test(self):
        return 0

संपादित करें: ध्यान दें कि प्रत्येक मामले में एक उदाहरण के Dविभिन्न मूल्य होंगे test। पहले मामले में d.test20 होगा, दूसरे में यह 0. होगा मैं इसे बाहर छोड़ दूँगा ताकि आप काम कर सकें।

Edit2: ग्रेग ने कहा कि उदाहरण 2 विफल हो जाएगा क्योंकि संपत्ति केवल पढ़ी जाती है और __init__विधि ने इसे 20 पर सेट करने की कोशिश की है। इसके लिए एक और पूर्ण उदाहरण होगा:

class D(object):
    def __init__(self):
        self.test = 20
        self.test2 = 21

    _test = 0

    def get_test(self):
        return self._test

    def set_test(self, value):
        self._test = value

    test = property(get_test, set_test)

जाहिर है, एक वर्ग के रूप में यह लगभग पूरी तरह से बेकार है, लेकिन यह आपको आगे बढ़ने का विचार देता है।


ओह, जब आप क्लास चलाते हैं तो चुपचाप काम नहीं करते हैं, नहीं? फ़ाइल "Script1.py", पंक्ति 5, init self.test = 20 गुणसमूह: विशेषता सेट नहीं कर सकते
ग्रेग

सच। मैं इसे तीसरे उदाहरण के रूप में ठीक करूँगा। अच्छी तरह से देखा गया।
सिंगलटन

5

यहाँ एक और अधिक विश्वसनीय संस्करण है:

class D(object):
    def __init__(self):
        self.test = 20
        self.test2 = 21
    def __getattribute__(self, name):
        if name == 'test':
            return 0.
        else:
            return super(D, self).__getattribute__(name)

यह माता-पिता वर्ग से __ getattribute __ विधि कहता है , अंततः वस्तु पर वापस गिर रहा है। यदि अन्य पूर्वजों ने इसे ओवरराइड नहीं किया है तो __ गेटएट्यूट __ विधि।


5

__getattribute__विधि का उपयोग कैसे किया जाता है?

इसे सामान्य बिंदीदार लुकअप से पहले कहा जाता है। अगर यह उठता है AttributeError, तो हम कॉल करते हैं __getattr__

इस विधि का उपयोग बल्कि दुर्लभ है। मानक पुस्तकालय में केवल दो परिभाषाएँ हैं:

$ grep -Erl  "def __getattribute__\(self" cpython/Lib | grep -v "/test/"
cpython/Lib/_threading_local.py
cpython/Lib/importlib/util.py

सर्वश्रेष्ठ प्रणालियां

किसी एकल विशेषता तक प्रोग्रामेटिक रूप से पहुंच को नियंत्रित करने का उचित तरीका है property। क्लास Dको निम्नानुसार लिखा जाना चाहिए (स्पष्ट उद्देश्य व्यवहार को दोहराने के लिए सेटर और डिलेटर के साथ वैकल्पिक रूप से):

class D(object):
    def __init__(self):
        self.test2=21

    @property
    def test(self):
        return 0.

    @test.setter
    def test(self, value):
        '''dummy function to avoid AttributeError on setting property'''

    @test.deleter
    def test(self):
        '''dummy function to avoid AttributeError on deleting property'''

और उपयोग:

>>> o = D()
>>> o.test
0.0
>>> o.test = 'foo'
>>> o.test
0.0
>>> del o.test
>>> o.test
0.0

एक संपत्ति एक डेटा डिस्क्रिप्टर है, इस प्रकार यह पहली चीज है जो सामान्य बिंदीदार लुकिंग एल्गोरिथ्म में देखी गई है।

के लिए विकल्प __getattribute__

यदि आपके पास हर विशेषता के लिए लुकअप को लागू करने की आवश्यकता है तो आपके पास कई विकल्प हैं __getattribute__

  • उठाएं AttributeError, जिसके कारण __getattr__कॉल किया जाएगा (यदि कार्यान्वित किया गया है)
  • इसके द्वारा कुछ लौटाओ
    • superमाता-पिता (शायद object) के कार्यान्वयन को कॉल करने के लिए उपयोग करना
    • बुला __getattr__
    • किसी भी तरह से अपने स्वयं के बिंदीदार एल्गोरिथ्म को लागू करना

उदाहरण के लिए:

class NoisyAttributes(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self, name):
        print('getting: ' + name)
        try:
            return super(NoisyAttributes, self).__getattribute__(name)
        except AttributeError:
            print('oh no, AttributeError caught and reraising')
            raise
    def __getattr__(self, name):
        """Called if __getattribute__ raises AttributeError"""
        return 'close but no ' + name    


>>> n = NoisyAttributes()
>>> nfoo = n.foo
getting: foo
oh no, AttributeError caught and reraising
>>> nfoo
'close but no foo'
>>> n.test
getting: test
20

जो आप मूल रूप से चाहते थे।

और यह उदाहरण दिखाता है कि आप मूल रूप से जो चाहते थे, वह कैसे कर सकते हैं:

class D(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self,name):
        if name=='test':
            return 0.
        else:
            return super(D, self).__getattribute__(name)

और इस तरह व्यवहार करेंगे:

>>> o = D()
>>> o.test = 'foo'
>>> o.test
0.0
>>> del o.test
>>> o.test
0.0
>>> del o.test

Traceback (most recent call last):
  File "<pyshell#216>", line 1, in <module>
    del o.test
AttributeError: test

को़ड समीक्षा

टिप्पणियों के साथ आपका कोड। आपके पास स्वयं में एक बिंदीदार खोज है __getattribute__। यही कारण है कि आपको एक पुनरावृत्ति त्रुटि मिलती है। आप देख सकते हैं कि नाम क्या है "__dict__"और superवर्कअराउंड का उपयोग करें, लेकिन यह कवर नहीं करता है __slots__। मैं पाठक को एक अभ्यास के रूप में छोड़ दूँगा।

class D(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self,name):
        if name=='test':
            return 0.
        else:      #   v--- Dotted lookup on self in __getattribute__
            return self.__dict__[name]

>>> print D().test
0.0
>>> print D().test2
...
RuntimeError: maximum recursion depth exceeded in cmp
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.