2011-01-05 16 views
5

निम्नलिखित कोड पर एक नज़र डालें: कुछ मूलभूत मूल्यों से युक्त अपने स्वयं के शब्दकोश के साथ प्रत्येकपायथन में सुपरक्लास के क्लास गुणों का उपयोग कैसे करें?

class A(object): 
    defaults = {'a': 1} 

    def __getattr__(self, name): 
     print('A.__getattr__') 
     return self.get_default(name) 

    @classmethod 
    def get_default(cls, name): 
     # some debug output 
     print('A.get_default({}) - {}'.format(name, cls)) 
     try: 
      print(super(cls, cls).defaults) # as expected 
     except AttributeError: #except for the base object class, of course 
      pass 

     # the actual function body 
     try: 
      return cls.defaults[name] 
     except KeyError: 
      return super(cls, cls).get_default(name) # infinite recursion 
      #return cls.__mro__[1].get_default(name) # this works, though 

class B(A): 
    defaults = {'b': 2} 

class C(B): 
    defaults = {'c': 3} 


c = C() 
print('c.a =', c.a) 

मैं वर्गों के एक पदानुक्रम है। यदि किसी वर्ग के किसी उदाहरण में कोई विशेष विशेषता नहीं है, तो इसके लिए डिफ़ॉल्ट मान इसके बदले वापस किया जाना चाहिए। यदि विशेषता के लिए कोई डिफ़ॉल्ट मान वर्तमान वर्ग के defaults शब्दकोश में निहित नहीं है, तो सुपरक्लास के defaults शब्दकोश को खोजा जाना चाहिए।

मैं रिकर्सिव क्लास विधि get_default का उपयोग करके इसे कार्यान्वित करने की कोशिश कर रहा हूं। दुर्भाग्यवश, कार्यक्रम एक अनंत रिकर्सन में फंस गया है। super() की मेरी समझ में कमी है। __mro__ तक पहुंचकर, मैं इसे ठीक से काम करने के लिए प्राप्त कर सकता हूं, लेकिन मुझे यकीन नहीं है कि यह एक उचित समाधान है।

मुझे लगता है कि उत्तर this article में कहीं है, लेकिन मैं इसे अभी तक नहीं ढूंढ पाया। शायद मुझे मेटाक्लास का उपयोग करने का सहारा लेना चाहिए?

संपादित करें: मेरे आवेदन में, __getattr__ पहले self.base चेक करता है। यदि यह None नहीं है, तो वहां से विशेषता प्राप्त की जानी चाहिए। केवल दूसरे मामले में, एक डिफ़ॉल्ट मान वापस किया जाना चाहिए। मैं शायद __getattribute__ ओवरराइड कर सकता था। क्या यह बेहतर समाधान होगा?

संपादित करें 2: नीचे कार्यक्षमता का विस्तारित उदाहरण है जिसे मैं ढूंढ रहा हूं। वर्तमान में इसे __mro__ (यूनूटू के पहले सुझाव, मेरी मूल रिकर्सिव विधि के विपरीत) का उपयोग करके कार्यान्वित किया गया है। जब तक कि कोई अधिक सुरुचिपूर्ण समाधान का सुझाव नहीं दे सकता, मैं इस कार्यान्वयन का उपयोग करके खुश हूं। मै उम्मीद करता हू कि यह बातें अब साफ है।

class A(object): 
    defaults = {'a': 1} 

    def __init__(self, name, base=None): 
     self.name = name 
     self.base = base 

    def __repr__(self): 
     return self.name 

    def __getattr__(self, name): 
     print(" '{}' attribute not present in '{}'".format(name, self)) 
     if self.base is not None: 
      print(" getting '{}' from base ({})".format(name, self.base)) 
      return getattr(self.base, name) 
     else: 
      print(" base = None; returning default value") 
      return self.get_default(name) 

    def get_default(self, name): 
     for cls in self.__class__.__mro__: 
      try: 
       return cls.defaults[name] 
      except KeyError: 
       pass 
     raise KeyError 

class B(A): 
    defaults = {'b': 2} 

class C(B): 
    defaults = {'c': 3} 


c1 = C('c1') 
c1.b = 55 

print('c1.a = ...'); print(' ...', c1.a) # 1 
print(); print('c1.b = ...'); print(' ...', c1.b) # 55 
print(); print('c1.c = ...'); print(' ...', c1.c) # 3 

c2 = C('c2', base=c1) 
c2.c = 99 

print(); print('c2.a = ...'); print(' ...', c2.a) # 1 
print(); print('c2.b = ...'); print(' ...', c2.b) # 55 
print(); print('c2.c = ...'); print(' ...', c2.c) # 99 

उत्पादन:

c1.a = ... 
'a' attribute not present in 'c1' 
    base = None; returning default value 
    ... 1 

c1.b = ... 
    ... 55 

c1.c = ... 
'c' attribute not present in 'c1' 
    base = None; returning default value 
    ... 3 

c2.a = ... 
'a' attribute not present in 'c2' 
    getting 'a' from base (c1) 
'a' attribute not present in 'c1' 
    base = None; returning default value 
    ... 1 

c2.b = ... 
'b' attribute not present in 'c2' 
    getting 'b' from base (c1) 
    ... 55 

c2.c = ... 
    ... 99 
+4

शब्दकोश 'डिफ़ॉल्ट' शब्द का उपयोग करने के बजाय डिफ़ॉल्ट रूप से कक्षा नामस्थान में डिफ़ॉल्ट को रखने के बारे में कैसे? इससे कोई जादू अनावश्यक हो जाएगा - यह सिर्फ काम करेगा। –

+0

@ स्वेन मुझे पता था कि मुझे इसका उल्लेख करना चाहिए था :) मेरे आवेदन में, '__getattr__' पहले एक और विशेषता (आधार) की जांच कर रहा है। केवल अगर अन्य विशेषता कोई नहीं है, तो डिफ़ॉल्ट मान वापस किया जाना चाहिए। अन्यथा, विशेषता को आधार में देखने की आवश्यकता है। –

+0

हाँ, इस तरह पाइथन में उदाहरण/वर्ग विशेषताएँ काम करती हैं। :) टोनी के जवाब की जांच करें। अफैक उसका कोड बिल्कुल आपके जैसा ही करेगा। –

उत्तर

0

प्रश्न के दूसरे संपादन में प्रस्तावित समाधान अभी भी एकमात्र ऐसा है जो मेरे आवेदन की ज़रूरतों को पूरा करता है। जबकि unutbu का कोड समझने में आसान हो सकता है, __mro__ समाधान आईएमओ के कुछ फायदे प्रदान करता है (टिप्पणियां देखें)।

0

आप name mangling यहाँ का उपयोग करना चाहिए।

यह मैं हूँ, एक करने के लिए overengineered दिखता है:

नाम बदलें defaults__defaults

कि प्रत्येक वर्ग के एक स्पष्ट विशेषता देता है तो वे एक दूसरे को

8

नहीं वास्तव में एक जवाब है, लेकिन एक अवलोकन के साथ भ्रमित नहीं कर रहे हैं करने के लिए पाइथन जादू का उपयोग करने के बहाने की तलाश करते समय आम जाल।

यदि आपको कक्षा के लिए defaults dict परिभाषित करने के लिए परेशान किया जा सकता है तो क्यों न केवल गुणों को परिभाषित करें? प्रभाव वही है।

class A: 
    a = 1 

class B(A): 
    b = 2 

class C(B): 
    c = 3 


c = C() 
print('c.a =', c.a) 

संपादित करें:

सवाल का जवाब देने के लिए के रूप में, मैं शायद __getattribute__ संयोजन में मेरी सुझाव के साथ इस तरह का प्रयोग करेंगे:

def __getattribute__(self, name): 
    try: 
     return object.__getattribute__(self.base, name) 
    except AttributeError: 
     return object.__getattribute__(self, name) 
+0

यह उल्लेखनीय है कि आप सी पर भी विशेषता सेट कर सकते हैं। ताकि आप कक्षा में परिभाषित डिफ़ॉल्ट के साथ अभ्यास में एक इंस्टेंस सेटिंग प्राप्त कर सकें। –

+0

यह प्रश्न में जोड़े गए उपयोग के मामले को पूरा नहीं करेगा। –

+0

@ ब्रैच माचियल्स: हाँ यह होगा। कोशिश करो। –

1

कैसे के बारे में:

class A(object): 
    def __init__(self,base=None): 
     self.a=1 
     if base is not None: 
      self.set_base(base) 
     super(A,self).__init__() 
    def set_base(self,base): 
     for key in ('a b c'.split()): 
      setattr(self,key,getattr(base,key)) 
class B(A): 
    def __init__(self,base=None): 
     self.b=2 
     super(B,self).__init__(base)   
class C(B): 
    def __init__(self,base=None): 
     self.c=3 
     super(C,self).__init__(base) 

c1=C() 
c1.b=55 
print(c1.a) 
print(c1.b) 
print(c1.c) 
# 1 
# 55 
# 3 

c2=C(c1) 
c2.c=99 
print(c2.a) 
print(c2.b) 
print(c2.c) 
# 1 
# 55 
# 99 

c1.set_base(c2) 
print(c1.a) 
print(c1.b) 
print(c1.c) 
# 1 
# 55 
# 99 
+0

यदि आधार * नहीं * 'कोई नहीं 'है, तो उसे आधार की संबंधित विशेषता वापस करने की आवश्यकता है। लेकिन मुझे लगता है कि हम रैपर समारोह में विशेषता का नाम नहीं प्राप्त कर सकते हैं? –

+0

@ ब्रैच माचियल्स: 'check_base_first' फ़ंक्शन पर अतिरिक्त पैरामीटर पारित करना वास्तव में संभव है, जिसे 'रैपर' से एक्सेस किया जा सकता है। – unutbu

+1

@ ब्रैच माचियल्स: यद्यपि आप जो चाहते हैं उसे करने के लिए गुणों का उपयोग करने का कोई तरीका हो सकता है, यह सही समाधान नहीं हो सकता है। जिस समस्या का आप सामना कर रहे हैं वह पहले खराब डिजाइन निर्णय का एक लक्षण हो सकता है। यदि आप अपने प्रश्न को संशोधित करने के लिए संशोधित करते हैं कि आप अपनी ऑब्जेक्ट्स को कैसे व्यवहार करना चाहते हैं ('आधार' के साथ) शायद एक बेहतर डिज़ाइन का सुझाव दिया जा सकता है। – unutbu

2

मुझे लगता है कि मुसीबत से परेशानी का परिणाम है super() के उद्देश्य को समझना।

http://docs.python.org/library/functions.html#super

अनिवार्य रूप से, सुपर में अपने ऑब्जेक्ट (या वर्ग)() लपेटकर अजगर की सबसे हाल में वंशागत वर्ग को छोड़ जब विशेषता लुकअप कर बनाता है। आपके कोड में, परिणामस्वरूप सी को get_default की तलाश करते समय छोड़ा जा रहा है, लेकिन यह वास्तव में कुछ भी नहीं करता है, क्योंकि सी किसी भी get_default को परिभाषित नहीं करता है। स्वाभाविक रूप से, इसका परिणाम अनंत लूप में होता है।

class DefaultsClass(type): 
    def __init__(cls, name, bases, dct): 

     def get_default(self, name): 
      # some debug output 
      print('A.get_default(%s) - %s' % (name, cls)) 
      try: 
       print(cls.defaults) # as expected 
      except AttributeError: #except for the base object class, of course 
       pass 

      # the actual function body 
      try: 
       return cls.defaults[name] 
      except KeyError: 
       return super(cls, self).get_default(name) # cooperative superclass 

     cls.get_default = get_default 
     return super(DefaultsClass, cls).__init__(name, bases, dct) 

class A(object): 
    defaults = {'a': 1} 
    __metaclass__ = DefaultsClass 

    def __getattr__(self, name): 
     return self.get_default(name) 



class B(A): 
    defaults = {'b': 2} 

class C(B): 
    defaults = {'c': 3} 


c = C() 
print('c.a =', c.a) 
print('c.b =', c.b) 
print('c.c =', c.c) 

परिणाम::

A.get_default(c) - <class '__main__.C'> 
{'c': 3} 
('c.c =', 3) 
A.get_default(b) - <class '__main__.C'> 
{'c': 3} 
A.get_default(b) - <class '__main__.B'> 
{'b': 2} 
('c.b =', 2) 
A.get_default(a) - <class '__main__.C'> 
{'c': 3} 
A.get_default(a) - <class '__main__.B'> 
{'b': 2} 
A.get_default(a) - <class '__main__.A'> 
{'a': 1} 
('c.a =', 1) 

मुझे लगता है कि सबसे ध्यान देना चाहिए अजगर

समाधान प्रत्येक वर्ग कि ए से निकला यह एक metaclass उपयोग किया जा सकता में इस समारोह को परिभाषित करने के लिए है लोग इसे एक बहुत ही विचित्र समाधान मानेंगे, और आपको केवल वास्तव में की आवश्यकता है, शायद विरासत कोड का समर्थन करने के लिए।

1

अपने "बेस" बनाम "डिफ़ॉल्ट" मामले के बारे में अधिक स्पष्ट होने के लिए।

>>> class A(object): 
...  a = 1 
... 
>>> class B(A): 
...  b = 2 
... 
>>> class C(B): 
...  c = 3 
... 
>>> a = A() 
>>> b = B() 
>>> c = C() 
>>> 
>>> b.b = 23 
>>> b.a 
1 
>>> b.b 
23 
>>> c.a 
1 
>>> c.b 
2 
>>> c.c 
3 
>>> c.c = 45 
>>> c.c 
45 

इसमें आपके निर्दिष्ट उपयोग मामले को शामिल किया गया है। आपको बिल्कुल कोई जादूगर की आवश्यकता नहीं है। यदि आपका उपयोग कुछ अलग है, तो समझाएं कि यह क्या है, और हम आपको बताएंगे कि बिना किसी जादूगर के इसे कैसे किया जाए। ;)

संबंधित मुद्दे