2013-05-07 6 views
15

मैं पाइथन क्लास की __setattr__ विधि को ओवरराइड करने का प्रयास कर रहा हूं, क्योंकि जब भी एक आवृत्ति विशेषता उसके मान को बदलती है तो मैं एक और फ़ंक्शन कॉल करना चाहता हूं। हालांकि, मैं __init__ विधि में इस व्यवहार नहीं करना चाहते, क्योंकि इस आरंभीकरण के दौरान मैं कुछ गुण है जो बाद में इस्तेमाल किया जा जा रहे हैं सेट:, अब तक मैं इस समाधान हैरनटाइम पर __setattr__ ओवरराइडिंग

रनटाइम पर __setattr__ अधिभावी बिना:

class Foo(object): 
    def __init__(self, a, b): 
     self.a = a 
     self.b = b 
     result = self.process(a) 
     for key, value in result.items(): 
      setattr(self, key, value) 
     # override self.__setattr__ here 

    def aux(self, name, value): 
     print(self.b) 
     object.__setattr__(self, name, value) 

मैं ०१२३१३४२१० साथ की कोशिश की है:

class Foo(object): 
    def __init__(self, a, host): 
     object.__setattr__(self, 'a', a) 
     object.__setattr__(self, 'b', b) 
     result = self.process(a) 
     for key, value in result.items(): 
      object.__setattr__(self, key, value) 

    def __setattr__(self, name, value): 
     print(self.b) # Call to a function using self.b 
     object.__setattr__(self, name, value) 

हालांकि, मैं इन object.__setattr__(...) से बचने और __init__ विधि के अंत में __setattr__ ओवरराइड करना चाहेंगेऔर object.__setitem__['__setitem__'] = self.aux, लेकिन इनमें से कोई भी प्रयास प्रभावशाली नहीं है। मैंने this section of the data model reference पढ़ा है, लेकिन ऐसा लगता है कि __setattr__ का असाइनमेंट थोड़ा मुश्किल है।

कैसे __init__ के अंत में __setattr__ ओवरराइड करने के लिए संभव हो सकता है, या कम से कम एक pythonic समाधान जहां __setattr__ केवल निर्माता में सामान्य तरीके से कहा जाता है?

उत्तर

20

दुर्भाग्य से, "ओवरराइड, इनिट के बाद" पायथन विशेष तरीकों का कोई तरीका नहीं है; उस लुकअप के काम के दुष्प्रभाव के रूप में। समस्या का क्रूक्स यह है कि पाइथन वास्तव में उदाहरण को नहीं देखता है; अपनी कक्षा को छोड़कर; इससे पहले कि यह विशेष विधि को देखना शुरू कर देता है; इसलिए ऑब्जेक्ट की स्थिति को प्रभावित करने के लिए कोई तरीका नहीं है कि कौन सी विधि देखी गई है।

यदि आपको __init__ में विशेष व्यवहार पसंद नहीं है, तो आप इसके बारे में विशेष जानकारी __setattr__ में रखने के लिए अपने कोड को दोबारा कर सकते हैं। कुछ की तरह:

class Foo(object): 
    __initialized = False 
    def __init__(self, a, b): 
     try: 
      self.a = a 
      self.b = b 
      # ... 
     finally: 
      self.__initialized = True 

    def __setattr__(self, attr, value): 
     if self.__initialzed: 
      print(self.b) 
     super(Foo, self).__setattr__(attr, value) 

संपादित करें: वास्तव में, वहाँ एक रास्ता है जो विशेष विधि ऊपर देखा है, इतने लंबे समय के बाद यह शुरू कर दिया गया आप अपने वर्ग बदलने के रूप में बदलने के लिए है। यह दृष्टिकोण metaclasses के मातम में अब तक आप भेजेगा, जिससे आगे विवरण के बिना, यहाँ है कि कैसे दिखाई देता है:

class AssignableSetattr(type): 
    def __new__(mcls, name, bases, attrs): 
     def __setattr__(self, attr, value): 
      object.__setattr__(self, attr, value) 

     init_attrs = dict(attrs) 
     init_attrs['__setattr__'] = __setattr__ 

     init_cls = super(AssignableSetattr, mcls).__new__(mcls, name, bases, init_attrs) 

     real_cls = super(AssignableSetattr, mcls).__new__(mcls, name, (init_cls,), attrs) 
     init_cls.__real_cls = real_cls 

     return init_cls 

    def __call__(cls, *args, **kwargs): 
     self = super(AssignableSetattr, cls).__call__(*args, **kwargs) 
     print "Created", self 
     real_cls = cls.__real_cls 
     self.__class__ = real_cls 
     return self 


class Foo(object): 
    __metaclass__ = AssignableSetattr 

    def __init__(self, a, b): 
     self.a = a 
     self.b = b 
     for key, value in process(a).items(): 
      setattr(self, key, value) 

    def __setattr__(self, attr, value): 
     frob(self.b) 
     super(Foo, self).__setattr__(attr, value) 


def process(a): 
    print "processing" 
    return {'c': 3 * a} 


def frob(x): 
    print "frobbing", x 


myfoo = Foo(1, 2) 
myfoo.d = myfoo.c + 1 
+1

वाह !!! बस वाह। मैंने इस पर ठोकर खाई और लगभग इसे खारिज कर दिया, लेकिन जैसा कि यह पता चला है कि मुझे वही समस्या हल करने की ज़रूरत है। इस जवाब में कुछ गंभीर पायथन जादू। क्षमा करें मैं 100 बार ऊपर नहीं उठा सकता: पी – velis

3

@ SingleNegationElimination के जवाब महान है, लेकिन यह inheritence के साथ काम नहीं कर सकते हैं, बच्चे वर्ग के __mro__ दुकान के मूल के बाद से सुपर क्लास की कक्षा। थोड़ा बदलाव के साथ, उनके जवाब से प्रेरित,

विचार सरल है, __init__ से पहले स्विच करें, और __init__ पूरा होने के बाद इसे वापस पुनर्स्थापित करें।

class CleanSetAttrMeta(type): 
    def __call__(cls, *args, **kwargs): 
     real_setattr = cls.__setattr__ 
     cls.__setattr__ = object.__setattr__ 
     self = super(CleanSetAttrMeta, cls).__call__(*args, **kwargs) 
     cls.__setattr__ = real_setattr 
     return self 


class Foo(object): 
    __metaclass__ = CleanSetAttrMeta 

    def __init__(self): 
     super(Foo, self).__init__() 
     self.a = 1 
     self.b = 2 

    def __setattr__(self, key, value): 
     print 'after __init__', self.b 
     super(Foo, self).__setattr__(key, value) 


class Bar(Foo): 
    def __init__(self): 
     super(Bar, self).__init__() 
     self.c = 3 

>>> f = Foo() 
>>> f.a = 10 
after __init__ 2 
>>> 
>>> b = Bar() 
>>> b.c = 30 
after __init__ 2 
संबंधित मुद्दे