2009-03-25 18 views
98

पायथन 2.5 में, एक सजावट बनाने वाला एक तरीका है जो कक्षा को सजाने वाला है? विशेष रूप से, मैं एक सदस्य को एक वर्ग में जोड़ने के लिए एक सजावटी का उपयोग करना चाहता हूं और उस सदस्य के लिए मूल्य लेने के लिए निर्माता को बदलना चाहता हूं।पायथन कक्षा सजावट

की तरह कुछ के लिए खोज रहे निम्नलिखित (जिस पर एक सिंटैक्स त्रुटि है 'वर्ग फू:':

def getId(self): return self.__id 

class addID(original_class): 
    def __init__(self, id, *args, **kws): 
     self.__id = id 
     self.getId = getId 
     original_class.__init__(self, *args, **kws) 

@addID 
class Foo: 
    def __init__(self, value1): 
     self.value1 = value1 

if __name__ == '__main__': 
    foo1 = Foo(5,1) 
    print foo1.value1, foo1.getId() 
    foo2 = Foo(15,2) 
    print foo2.value1, foo2.getId() 

धन्यवाद, रोब

संपादित करें: इस सवाल फिर से पढ़ने पर, मुझे लगता है क्या मैं वास्तव में पाइथन में सी # इंटरफेस की तरह कुछ करने का एक तरीका है। मुझे लगता है कि मुझे अपना प्रतिमान स्विच करना होगा।

+34

हालांकि यह उपयोगी है, पाइथन सजावट सजावटी पैटर्न से अलग हैं। मुझे लगता है कि एक दुर्भाग्यपूर्ण नाम। हालांकि, मैं इस तरह से जा रहा हूँ। धन्यवाद। –

+13

@ रॉबर्ट गौलैंड: बहुत राजनयिक। –

उत्तर

65

मैं इस धारणा को दूसरी बार दूंगा कि आप जिस दृष्टिकोण के बारे में बताए गए दृष्टिकोण के बजाय उप-वर्ग पर विचार करना चाहेंगे। हालांकि, अपने विशिष्ट परिदृश्य को नहीं जानते, वाईएमएमवी :-)

आप जो सोच रहे हैं वह मेटाक्लास है। मेटाक्लास में __new__ फ़ंक्शन कक्षा की पूर्ण प्रस्तावित परिभाषा को पारित किया जाता है, जिसे क्लास के निर्माण से पहले फिर से लिखना पड़ सकता है। आप उस समय, एक नए के लिए कन्स्ट्रक्टर को बाहर कर सकते हैं।

उदाहरण:

def substitute_init(self, id, *args, **kwargs): 
    pass 

class FooMeta(type): 

    def __new__(cls, name, bases, attrs): 
     attrs['__init__'] = substitute_init 
     return super(FooMeta, cls).__new__(cls, name, bases, attrs) 

class Foo(object): 

    __metaclass__ = FooMeta 

    def __init__(self, value1): 
     pass 

निर्माता की जगह शायद थोड़ा नाटकीय है, लेकिन भाषा गहरे आत्मनिरीक्षण और गतिशील संशोधन के इस प्रकार के लिए सहायता प्रदान करता है।

+0

धन्यवाद-मैं, यही वह है जिसे मैं ढूंढ रहा हूं। एक वर्ग जो किसी भी अन्य वर्गों को संशोधित कर सकता है जैसे कि उनके पास एक विशेष सदस्य है। कक्षाओं को सामान्य आईडी कक्षा से प्राप्त करने के मेरे कारण यह नहीं है कि मैं कक्षाओं के साथ-साथ आईडी संस्करणों के गैर-आईडी संस्करण भी प्राप्त करना चाहता हूं। –

+0

मेटाक्लास इस तरह की चीजें पाइथन 2.5 या उससे अधिक उम्र में करने के लिए जाते थे, लेकिन आजकल आप कक्षा सजावट का उपयोग कर सकते हैं (स्टीवन का जवाब देखें), जो बहुत आसान हैं। –

11

यह एक अच्छा अभ्यास नहीं है और कोई मेक नहीं है इसके कारण ऐसा करने के लिए हनीवाद। जो भी आप चाहते हैं उसे पूरा करने का सही तरीका विरासत है।

class documentation पर एक नज़र डालें।

एक छोटी सी उदाहरण:

class Employee(object): 

    def __init__(self, age, sex, siblings=0): 
     self.age = age 
     self.sex = sex  
     self.siblings = siblings 

    def born_on(self):  
     today = datetime.date.today() 

     return today - datetime.timedelta(days=self.age*365) 


class Boss(Employee):  
    def __init__(self, age, sex, siblings=0, bonus=0): 
     self.bonus = bonus 
     Employee.__init__(self, age, sex, siblings) 

इस तरह बॉस है सब कुछ Employee भी अपने ही __init__ विधि और खुद के सदस्यों के साथ, है।

+3

मुझे लगता है कि मैं जो वर्ग चाहता था उसमें वर्ग के बॉस अज्ञेयवादी थे। यही है, कई अलग-अलग वर्ग हो सकते हैं जिन्हें मैं बॉस सुविधाओं को लागू करना चाहता हूं। क्या मैं इन दर्जन वर्गों को बॉस से प्राप्त करने के साथ छोड़ दिया गया हूं? –

+4

@ रॉबर्ट गौलैंड: यही कारण है कि पाइथन के पास एकाधिक विरासत है। हां, आपको विभिन्न अभिभावक वर्गों से विभिन्न पहलुओं का उत्तराधिकारी होना चाहिए। –

+7

@ एसएलओटी: सामान्य एकाधिक विरासत में एक बुरा विचार है, विरासत का भी बहुत अधिक स्तर भी बुरा है। मैं आपको कई विरासत से दूर रहने की सलाह दूंगा। – mpeterson

176

अलावा सवाल यह है कि वर्ग सज्जाकार आपकी समस्या का सही समाधान कर रहे हैं से:

अजगर 2.6 और में

अधिक है, वहाँ है, तो आप लिख सकते हैं @ -syntax साथ वर्ग सज्जाकार हैं:

@addID 
class Foo: 
    pass 

class Foo: 
    pass 

Foo = addID(Foo) 

नोट तथापि है कि इस समारोह सज्जाकार के लिए के रूप में ही काम करता है, और कहा कि डेकोरेटर आर चाहिए: पुराने संस्करणों में

, आप इसे दूसरे तरीके से कर सकते हैं नया (या संशोधित मूल) वर्ग तैयार करें, जो आप उदाहरण में नहीं कर रहे हैं। addID डेकोरेटर इस प्रकार दिखाई देगा:

def addID(original_class): 
    orig_init = original_class.__init__ 
    # make copy of original __init__, so we can call it without recursion 

    def __init__(self, id, *args, **kws): 
     self.__id = id 
     self.getId = getId 
     orig_init(self, *args, **kws) # call the original __init__ 

    original_class.__init__ = __init__ # set the class' __init__ to the new one 
    return original_class 

फिर आप जैसा कि ऊपर वर्णित अपने अजगर संस्करण के लिए उपयुक्त सिंटैक्स का उपयोग कर सकते हैं।

लेकिन मैं दूसरों से सहमत हूं कि यदि आप __init__ ओवरराइड करना चाहते हैं तो विरासत बेहतर अनुकूल है।

+2

... भले ही प्रश्न विशेष रूप से पायथन 2.5 का उल्लेख करता है :-) –

+0

आपका उदाहरण एक रनटाइम त्रुटि का कारण बनता है: अधिकतम रिकर्सन गहराई पार हो जाती है, क्योंकि जब कक्षा को तत्काल किया जाता है, तो मूल_क्लास .__ init__ वह फ़ंक्शन है जिसे कॉल किया जाता है, इसलिए __init__ स्वयं को कॉल करता है। मैं अगली टिप्पणी में एक कामकाजी उदाहरण पोस्ट करूंगा। –

+5

linesep गंदगी के लिए क्षमा करें, लेकिन कोड नमूने टिप्पणी में सख्ती से महान नहीं हैं ...: डीईएफ़ addID (original_class): original_class .__ orig__init__ = original_class .__ init__ डीईएफ़ __init __ (स्वयं, * args, ** KWS): प्रिंट "डेकोरेटर" self.id = 9 original_class .__ orig__init __ (स्वयं, * args, ** KWS) original_class .__ init__ = __init__ वापसी original_class @addID वर्ग फू: डीईएफ़ __init __ (आत्म): प्रिंट "फू" ए = फू() प्रिंट a.id –

4

वहाँ वास्तव में एक वर्ग डेकोरेटर यहाँ की एक बहुत अच्छी कार्यान्वयन है:

https://github.com/agiliq/Django-parsley/blob/master/parsley/decorators.py

मैं वास्तव में लगता है कि यह एक बहुत ही दिलचस्प कार्यान्वयन है। चूंकि यह वर्ग को सजाने वाले वर्ग को उप-वर्ग बनाता है, यह isinstance चेक जैसी चीजों में इस वर्ग की तरह व्यवहार करेगा।

यह है एक अतिरिक्त लाभ: यह असामान्य एक कस्टम Django फार्म में __init__ बयान self.fields के संशोधन या संयोजन तो यह self.fields में परिवर्तन__init__ के सभी के बाद होने की बेहतर में वर्ग के लिए समाप्त हो गया है है बनाने के लिए के लिए नहीं है सवाल।

बहुत चालाक।

हालांकि, आपकी कक्षा में आप वास्तव में सजावट को बदलने के लिए सजावट चाहते हैं, जो मुझे नहीं लगता कि कक्षा सजावट के लिए एक अच्छा उपयोग मामला है।

10

कोई भी व्यक्ति ने समझाया है कि आप कक्षाओं को गतिशील रूप से परिभाषित कर सकते हैं।

def addId(cls): 

    class AddId(cls): 

     def __init__(self, id, *args, **kargs): 
      super(AddId, self).__init__(*args, **kargs) 
      self.__id = id 

     def getId(self): 
      return self.__id 

    return AddId 

जो अजगर 2 इस तरह (बताते हैं यही वजह है कि आप 2.6+ में यह करने के लिए जारी रखना चाहिए Blckknght से टिप्पणी) में इस्तेमाल किया जा सकता: ताकि आप एक डेकोरेटर कि परिभाषित करता है (और रिटर्न) एक उपवर्ग हो सकता है:

class Foo: 
    pass 

FooId = addId(Foo) 

और अजगर 3 इस तरह में (लेकिन अपनी कक्षाओं में super() उपयोग करने के लिए सावधान रहना होगा):

@addId 
class Foo: 
    pass 

तो आप अपने केक हो सकता है और इसे खाएं - विरासत और सजावटी!

+2

एक सजावट में उप-वर्गीकरण पायथन 2.6+ में खतरनाक है, क्योंकि यह मूल वर्ग में 'सुपर' कॉल तोड़ता है। यदि 'फू' में 'foo' नामक एक विधि थी जिसे 'सुपर (फू, सेल्फ)। Foo()' कहा जाता है, तो यह असीमित रूप से पुन: कार्य करेगा, क्योंकि 'फू' नाम सजावटी द्वारा लौटाए गए उप-वर्ग से जुड़ा हुआ है, न कि मूल वर्ग (जो किसी भी नाम से सुलभ नहीं है)। पायथन 3 की बहसहीन 'सुपर()' इस मुद्दे से बचाती है (मैं उसी कंपाइलर जादू के माध्यम से मानता हूं जो इसे बिल्कुल काम करने की अनुमति देता है)। आप विभिन्न नामों के तहत कक्षा को मैन्युअल रूप से सजाने के द्वारा इस मुद्दे के आसपास भी काम कर सकते हैं (जैसा आपने पाइथन 2.5 उदाहरण में किया था)। – Blckknght

+1

हुह। धन्यवाद, मुझे नहीं पता था (मैं अजगर 3 का उपयोग करता हूं)। एक टिप्पणी जोड़ देंगे। –

0

मैं मानता हूं कि विरासत समस्या के लिए बेहतर फिट है।

मुझे यह सवाल वास्तव में सजाया गया है हालांकि सजाने वाले वर्गों पर, धन्यवाद।

def dec(klass): 
    old_foo = klass.foo 
    @wraps(klass.foo) 
    def decorated_foo(self, *args ,**kwargs): 
     print('@decorator pre %s' % msg) 
     old_foo(self, *args, **kwargs) 
     print('@decorator post %s' % msg) 
    klass.foo = decorated_foo 
    return klass 

@dec # no parentheses 
class Foo... 

अक्सर आप अपने डेकोरेटर के लिए मानकों को जोड़ना चाहते हैं: यहाँ एक और कुछ उदाहरण सहित कैसे विरासत अजगर 2.7, (और @wraps है, जो मूल कार्य के docstring, आदि का कहना है) में चीजों को प्रभावित करता है अन्य उत्तर, के आधार पर दिया गया है:

from functools import wraps 

def dec(msg='default'): 
    def decorator(klass): 
     old_foo = klass.foo 
     @wraps(klass.foo) 
     def decorated_foo(self, *args ,**kwargs): 
      print('@decorator pre %s' % msg) 
      old_foo(self, *args, **kwargs) 
      print('@decorator post %s' % msg) 
     klass.foo = decorated_foo 
     return klass 
    return decorator 

@dec('foo decorator') # you must add parentheses now, even if they're empty 
class Foo(object): 
    def foo(self, *args, **kwargs): 
     print('foo.foo()') 

@dec('subfoo decorator') 
class SubFoo(Foo): 
    def foo(self, *args, **kwargs): 
     print('subfoo.foo() pre') 
     super(SubFoo, self).foo(*args, **kwargs) 
     print('subfoo.foo() post') 

@dec('subsubfoo decorator') 
class SubSubFoo(SubFoo): 
    def foo(self, *args, **kwargs): 
     print('subsubfoo.foo() pre') 
     super(SubSubFoo, self).foo(*args, **kwargs) 
     print('subsubfoo.foo() post') 

SubSubFoo().foo() 

आउटपुट:

@decorator pre subsubfoo decorator 
subsubfoo.foo() pre 
@decorator pre subfoo decorator 
subfoo.foo() pre 
@decorator pre foo decorator 
foo.foo() 
@decorator post foo decorator 
subfoo.foo() post 
@decorator post subfoo decorator 
subsubfoo.foo() post 
@decorator post subsubfoo decorator 

मैं एक समारोह डेकोरेटर का उपयोग किया है, के रूप में मैं उन्हें अधिक संक्षिप्त पाते हैं। यहां कक्षा को सजाने के लिए एक वर्ग है:

class Dec(object): 

    def __init__(self, msg): 
     self.msg = msg 

    def __call__(self, klass): 
     old_foo = klass.foo 
     msg = self.msg 
     def decorated_foo(self, *args, **kwargs): 
      print('@decorator pre %s' % msg) 
      old_foo(self, *args, **kwargs) 
      print('@decorator post %s' % msg) 
     klass.foo = decorated_foo 
     return klass 

एक और अधिक मजबूत संस्करण है कि उन कोष्ठकों के लिए जाँच करता है, और काम करता है तरीकों से सजाया वर्ग पर मौजूद नहीं हैं:

from inspect import isclass 

def decorate_if(condition, decorator): 
    return decorator if condition else lambda x: x 

def dec(msg): 
    # Only use if your decorator's first parameter is never a class 
    assert not isclass(msg) 

    def decorator(klass): 
     old_foo = getattr(klass, 'foo', None) 

     @decorate_if(old_foo, wraps(klass.foo)) 
     def decorated_foo(self, *args ,**kwargs): 
      print('@decorator pre %s' % msg) 
      if callable(old_foo): 
       old_foo(self, *args, **kwargs) 
      print('@decorator post %s' % msg) 

     klass.foo = decorated_foo 
     return klass 

    return decorator 

assert जांच करता है कि सजावटी को बिना किसी ब्रांड्स के इस्तेमाल किया गया है।यदि यह है, तो सजाया जाने वाला वर्ग सजावट के msg पैरामीटर को पास कर दिया गया है, जो AssertionError उठाता है।

@decorate_ifdecorator लागू होता है यदि conditionTrue पर मूल्यांकन करता है।

getattr, callable परीक्षण, और @decorate_if उपयोग किया जाता है ताकि डेकोरेटर अगर foo() विधि वर्ग सजाया जा रहा है पर मौजूद नहीं है नष्ट नहीं होती है।

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