2011-03-04 21 views
62

पायथन में मैं @classmethod सजावट वाले वर्ग में एक विधि जोड़ सकता हूं। क्या कक्षा में संपत्ति जोड़ने के लिए एक समान सजावट है? मैं बेहतर दिखा सकता हूं कि मैं किस बारे में बात कर रहा हूं।कक्षा संपत्ति कैसे बनाएं?

class Example(object): 
    the_I = 10 
    def __init__(self): 
     self.an_i = 20 

    @property 
    def i(self): 
     return self.an_i 

    def inc_i(self): 
     self.an_i += 1 

    # is this even possible? 
    @classproperty 
    def I(cls): 
     return cls.the_I 

    @classmethod 
    def inc_I(cls): 
     cls.the_I += 1 

e = Example() 
assert e.i == 20 
e.inc_i() 
assert e.i == 21 

assert Example.I == 10 
Example.inc_I() 
assert Example.I == 11 

क्या वाक्यविन्यास मैंने ऊपर से उपयोग किया है या क्या इसे और कुछ चाहिए?

कारण मैं कक्षा गुण चाहता हूं इसलिए मैं आलसी लोड वर्ग विशेषताओं को लोड कर सकता हूं, जो पर्याप्त उचित लगता है।

+1

मुझे पायथन नहीं पता ... क्या यह ऐसी चीज है जिसे आप ढूंढ रहे हैं? http://www.python.org/download/releases/2।2/descrintro/# संपत्ति –

+0

यदि मैं इसे सही पढ़ रहा हूं, तो आप सेटर और गेटर (अन्य भाषाओं में) के रूप में कार्य करने के तरीकों को बना सकते हैं ताकि आप पहली बार संपत्ति मूल्य को आलसी लोड कर सकें ... जो मुझे लगता है क्या आप चाहते थे? –

+0

@ व्हाइट ड्रैगन। जिस संपत्ति सुविधा पर आप देख रहे हैं वह क्लास इंस्टेंस में गुण जोड़ता है, न कि कक्षाओं के लिए। मैं 'उदाहरण के बारे में पूछ रहा हूं। मैं' e.i' नहीं हूं। –

उत्तर

44

यहाँ मैं यह कैसे करना होगा है:

class ClassPropertyDescriptor(object): 

    def __init__(self, fget, fset=None): 
     self.fget = fget 
     self.fset = fset 

    def __get__(self, obj, klass=None): 
     if klass is None: 
      klass = type(obj) 
     return self.fget.__get__(obj, klass)() 

    def __set__(self, obj, value): 
     if not self.fset: 
      raise AttributeError("can't set attribute") 
     type_ = type(obj) 
     return self.fset.__get__(obj, type_)(value) 

    def setter(self, func): 
     if not isinstance(func, (classmethod, staticmethod)): 
      func = classmethod(func) 
     self.fset = func 
     return self 

def classproperty(func): 
    if not isinstance(func, (classmethod, staticmethod)): 
     func = classmethod(func) 

    return ClassPropertyDescriptor(func) 


class Bar(object): 

    _bar = 1 

    @classproperty 
    def bar(cls): 
     return cls._bar 

    @bar.setter 
    def bar(cls, value): 
     cls._bar = value 


# test instance instantiation 
foo = Bar() 
assert foo.bar == 1 

baz = Bar() 
assert baz.bar == 1 

# test static variable 
baz.bar = 5 
assert foo.bar == 5 

# test setting variable on the class 
Bar.bar = 50 
assert baz.bar == 50 
assert foo.bar == 50 

सेटर, बार हम कहते हैं Bar.bar पर काम नहीं किया क्योंकि हम TypeOfBar.bar.__set__, जो Bar.bar.__set__ नहीं है बुला रहे हैं।

एक metaclass परिभाषा जोड़ने से इस को हल करती है:

class ClassPropertyMetaClass(type): 
    def __setattr__(self, key, value): 
     if key in self.__dict__: 
      obj = self.__dict__.get(key) 
     if obj and type(obj) is ClassPropertyDescriptor: 
      return obj.__set__(self, value) 

     return super(ClassPropertyMetaClass, self).__setattr__(key, value) 

# and update class define: 
#  class Bar(object): 
#  __metaclass__ = ClassPropertyMetaClass 
#  _bar = 1 

# and update ClassPropertyDescriptor.__set__ 
# def __set__(self, obj, value): 
#  if not self.fset: 
#   raise AttributeError("can't set attribute") 
#  if inspect.isclass(obj): 
#   type_ = obj 
#   obj = None 
#  else: 
#   type_ = type(obj) 
#  return self.fset.__get__(obj, type_)(value) 

अब सब ठीक हो जाएगा।

+0

हम्म .. मेरे सेटर फ़ंक्शन को अनदेखा किया जा रहा है, और मुझे कभी भी निष्पादित करने के लिए __set__ में कुछ भी नहीं मिल सकता है (यहां तक ​​कि अगर मैं वहां कुछ कचरा भी बदलता हूं, कुछ भी नहीं बदलता है) –

+0

दूसरा दावा यहां विफल रहता है: http: // drktd। कॉम/6 डी 2 बी –

+0

सेटर के साथ काम नहीं करता है। – I159

22

मुझे लगता है कि आप मेटाक्लास के साथ ऐसा करने में सक्षम हो सकते हैं। चूंकि मेटाक्लास वर्ग के लिए कक्षा की तरह हो सकता है (यदि यह समझ में आता है)। मुझे पता है कि आप क्लास, MyClass() पर कॉल करने के लिए मेटाक्लास को __call__() विधि असाइन कर सकते हैं। मुझे आश्चर्य है कि मेटाक्लास पर property सजावटी का उपयोग समान रूप से संचालित होता है। (मैं इस से पहले की कोशिश की है, लेकिन अब मैं उत्सुक हूँ ...)

[अद्यतन:]

वाह, यह करता है काम:

class MetaClass(type):  
    def getfoo(self): 
     return self._foo 
    foo = property(getfoo) 

    @property 
    def bar(self): 
     return self._bar 

class MyClass(object): 
    __metaclass__ = MetaClass 
    _foo = 'abc' 
    _bar = 'def' 

print MyClass.foo 
print MyClass.bar 

नोट: यह अजगर में है 2.7। पाइथन 3+ मेटाक्लास घोषित करने के लिए एक अलग तकनीक का उपयोग करता है। उपयोग करें: class MyClass(metaclass=MetaClass):, __metaclass__ हटाएं, और बाकी एक ही है।

+0

तो यह _can_ किया जा सकता है, लेकिन क्या इसे विधि सजावट के साथ किया जा सकता है? –

+0

कोई सजावट नहीं है, मुझे पता है कि आप जिस कक्षा में रुचि रखते हैं उस पर सीधे उपयोग कर सकते हैं। हालांकि, मेटाक्लास में 'प्रॉपर्टी 'सजावट काम करनी चाहिए .... मैंने अपना जवाब संपादित करने के लिए संपादित किया है मेटाक्लास विधि। – dappawit

+1

यह भी देखें [यह वही प्रश्न] (http://stackoverflow.com/questions/128573/using-property-on-classmethods) और इसके उत्तर। ऐसा लगता है, इसलिए इसमें कुछ और उपयोगी जानकारी हो सकती है :) – Abbafei

1

यदि आपको केवल आलसी लोडिंग की आवश्यकता है, तो आपके पास केवल कक्षा प्रारंभिक विधि हो सकती है।

EXAMPLE_SET = False 
class Example(object): 
    @classmethod 
    def initclass(cls): 
     global EXAMPLE_SET 
     if EXAMPLE_SET: return 
     cls.the_I = 'ok' 
     EXAMPLE_SET = True 

    def __init__(self): 
     Example.initclass() 
     self.an_i = 20 

try: 
    print Example.the_I 
except AttributeError: 
    print 'ok class not "loaded"' 
foo = Example() 
print foo.the_I 
print Example.the_I 

लेकिन मेटाक्लास दृष्टिकोण साफ दिखता है, और अधिक अनुमानित व्यवहार के साथ।

शायद आप जो खोज रहे हैं वह Singleton डिज़ाइन पैटर्न है। पाइथन में साझा राज्य को लागू करने के बारे में a nice SO QA है।

20

यदि आप निम्नानुसार classproperty परिभाषित करते हैं, तो आपका उदाहरण ठीक उसी तरह काम करता है जैसा आपने अनुरोध किया था।

class classproperty(object): 
    def __init__(self, f): 
     self.f = f 
    def __get__(self, obj, owner): 
     return self.f(owner) 

चेतावनी यह है कि आप इसे लिखने योग्य गुणों के लिए उपयोग नहीं कर सकते हैं। जबकि e.I = 20AttributeError बढ़ाएगा, Example.I = 20 संपत्ति ऑब्जेक्ट को ओवरराइट करेगा।

2

जहां तक ​​मैं कह सकता हूं, एक नया मेटाक्लास बनाने के बिना कक्षा संपत्ति के लिए एक सेटटर लिखने का कोई तरीका नहीं है।

मुझे पता चला है कि निम्न विधि काम करती है। सभी क्लास गुणों और सेटर्स के साथ मेटाक्लास को परिभाषित करें जो आप चाहते हैं।आईई, मैं एक वर्ग के साथ एक title संपत्ति के साथ एक वर्ग चाहता था। यहाँ मैं क्या लिखा है:

class TitleMeta(type): 
    @property 
    def title(self): 
     return getattr(self, '_title', 'Default Title') 

    @title.setter 
    def title(self, title): 
     self._title = title 
     # Do whatever else you want when the title is set... 

अब वास्तविक वर्ग आप सामान्य रूप में चाहते हैं, को छोड़कर यह metaclass जो आपने ऊपर बनाया का उपयोग करें।

# Python 2 style: 
class ClassWithTitle(object): 
    __metaclass__ = TitleMeta 
    # The rest of your class definition... 

# Python 3 style: 
class ClassWithTitle(object, metaclass = TitleMeta): 
    # Your class definition... 

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

10

[उत्तर पाइथन 3.4 पर आधारित उत्तर; मेटाक्लास सिंटैक्स 2 में भिन्न है लेकिन मुझे लगता है कि तकनीक अभी भी काम करेगी]

आप इसे मेटाक्लास के साथ कर सकते हैं ... ज्यादातर। Dappawit लगभग काम करता है, लेकिन मुझे लगता है कि इसमें कोई कमी नहीं है:

class MetaFoo(type): 
    @property 
    def thingy(cls): 
     return cls._thingy 

class Foo(object, metaclass=MetaFoo): 
    _thingy = 23 

यह आपको कम महत्वपूर्ण चीज़ों पर एक classproperty हो जाता है, लेकिन वहाँ एक समस्या है ...

print("Foo.thingy is {}".format(Foo.thingy)) 
# Foo.thingy is 23 
# Yay, the classmethod-property is working as intended! 
foo = Foo() 
if hasattr(foo, "thingy"): 
    print("Foo().thingy is {}".format(foo.thingy)) 
else: 
    print("Foo instance has no attribute 'thingy'") 
# Foo instance has no attribute 'thingy' 
# Wha....? 

क्या हो यहाँ चल रहा है? मैं एक उदाहरण से कक्षा संपत्ति तक क्यों नहीं पहुंच सकता?

मैं जो कुछ मानता हूं उसे ढूंढने से पहले मैं थोड़ी देर के लिए इस पर अपना सिर मार रहा था। अजगर @properties descriptor documentation (जोर मेरा) से वर्णनकर्ता का एक सबसेट है, और,: विशेषता पहुँच के लिए

डिफ़ॉल्ट व्यवहार मिलता है, सेट करें, या एक वस्तु के शब्दकोश से विशेषता हटाना है। उदाहरण के लिए, a.x एक देखने श्रृंखला a.__dict__['x'] के साथ शुरू है, तो type(a).__dict__['x'], और सतत type(a)छोड़कर metaclasses के आधार कक्षाओं के माध्यम से है।

तो विधि समाधान आदेश में हमारे वर्ग गुण (या मेटाक्लास में परिभाषित कुछ भी शामिल नहीं है) शामिल नहीं है। यह अंतर्निहित संपत्ति सजावट का एक उप-वर्ग बनाने के लिए संभव है जो अलग-अलग व्यवहार करता है, लेकिन (उद्धरण वांछित) मुझे इंप्रेशन गुगल रहा है कि डेवलपर्स के पास ऐसा करने के लिए एक अच्छा कारण था (जिसे मैं समझ नहीं पा रहा हूं) मार्ग।

इसका मतलब यह नहीं है कि हम भाग्य से बाहर हैं; हम वर्ग में ही ठीक पर गुण का उपयोग कर सकते ... और हम उदाहरण है, जो हम @property प्रेषकों बनाने के लिए उपयोग कर सकते हैं के भीतर type(self) से वर्ग प्राप्त कर सकते हैं: के रूप में दोनों के लिए लक्षित

class Foo(object, metaclass=MetaFoo): 
    _thingy = 23 

    @property 
    def thingy(self): 
     return type(self).thingy 

अब Foo().thingy काम करता है कक्षा और उदाहरण! यदि व्युत्पन्न वर्ग इसके अंतर्निहित _thingy (जो उपयोग केस है जो मुझे मूल रूप से इस शिकार पर मिला है) को बदल देता है तो यह सही काम भी जारी रखेगा।

यह मेरे लिए 100% संतुष्ट नहीं है - मेटाक्लास और ऑब्जेक्ट क्लास दोनों में सेटअप करने के लिए ऐसा लगता है जैसे यह DRY सिद्धांत का उल्लंघन करता है। लेकिन उत्तरार्द्ध सिर्फ एक लाइन प्रेषक है; मैं इसके साथ अधिकतर ठीक हूं, और यदि आप वास्तव में चाहते थे तो आप शायद इसे लैम्ब्डा या किसी चीज़ पर कॉम्पैक्ट कर सकते हैं।

+0

यह शीर्ष जवाब होना चाहिए यह सार्वभौमिक रूप से और उप-वर्गों के लिए भी काम करता है। – geckon

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