2016-03-16 7 views
20

पायथन 3.4 के रूप में, DynamicClassAttribute नामक एक वर्णक है। प्रलेखन राज्यों:डायनामिक क्लास एट्रिब्यूट क्या है और मैं इसका उपयोग कैसे करूं?

types.DynamicClassAttribute(fget=None, fset=None, fdel=None, doc=None)

मार्ग __getattr__ करने के लिए एक वर्ग पर पहुंच विशेषता।

यह एक वर्णक है, जो एक उदाहरण के माध्यम से और कक्षा के माध्यम से उपयोग किए जाने वाले गुणों को परिभाषित करने के लिए उपयोग किया जाता है। इंस्टेंस एक्सेस सामान्य रहता है, लेकिन कक्षा के माध्यम से एक विशेषता तक पहुंच कक्षा के __getattr__ विधि पर जा दी जाएगी; यह AttributeError उठाकर किया जाता है।

यह किसी एक उदाहरण पर गुणों को सक्रिय करने की अनुमति देता है, और उसी नाम के साथ कक्षा पर वर्चुअल विशेषताएं हैं (उदाहरण के लिए Enum देखें)।

संस्करण 3.4 में नया।

यह जाहिरा तौर पर enum module में प्रयोग किया जाता है:

# DynamicClassAttribute is used to provide access to the `name` and 
# `value` properties of enum members while keeping some measure of 
# protection from modification, while still allowing for an enumeration 
# to have members named `name` and `value`. This works because enumeration 
# members are not set directly on the enum class -- __getattr__ is 
# used to look them up. 

@DynamicClassAttribute 
def name(self): 
    """The name of the Enum member.""" 
    return self._name_ 

@DynamicClassAttribute 
def value(self): 
    """The value of the Enum member.""" 
    return self._value_ 

मुझे लगता है कि enums are a little special, लेकिन मुझे समझ नहीं आता कि यह कैसे DynamicClassAttribute से संबंधित है। इसका क्या अर्थ है कि उन गुण गतिशील हैं, यह सामान्य संपत्ति से अलग कैसे है, और मैं अपने लाभ के लिए DynamicClassAttribute का उपयोग कैसे करूं?

उत्तर

12

नया संस्करण:

मैं तो मैं यह थोड़ा पुनर्लेखन करने का निर्णय लिया पिछले जवाब के साथ एक सा निराश था:

पहले the source code of DynamicClassAttribute पर एक नजर है और आप शायद ध्यान देंगे, यह है कि यह बहुत लग रहा है सामान्य property की तरह। __get__ -method को छोड़कर:

def __get__(self, instance, ownerclass=None): 
    if instance is None: 
     # Here is the difference, the normal property just does: return self 
     if self.__isabstractmethod__: 
      return self 
     raise AttributeError() 
    elif self.fget is None: 
     raise AttributeError("unreadable attribute") 
    return self.fget(instance) 

तो क्या इसका मतलब यह है है कि आप का उपयोग करना चाहते हैं तो एक DynamicClassAttribute (कि सार नहीं है) वर्ग पर यह बजाय self लौटने का एक AttributeError को जन्म देती है। उदाहरण के लिए if instance:True का मूल्यांकन करता है और __get__property.__get__ के समान है।

सामान्य वर्गों है कि बस में निराकरण के लिए एक दिखाईAttributeError जब विशेषता बुला:

from types import DynamicClassAttribute 
class Fun(): 
    @DynamicClassAttribute 
    def has_fun(self): 
     return False 
Fun.has_fun 

AttributeError - Traceback (सबसे हाल कॉल पिछले)

कि खुद के लिए नहीं है जब तक आप metaclass es (I) का उपयोग करते हुए पर "क्लास एट्रिब्यूट लुकअप" प्रक्रिया पर नज़र डालें, तब तक बहुत उपयोगी this blog में इसकी एक अच्छी छवि मिली)। क्योंकि यदि कोई विशेषता AttributeError उठाती है और उस वर्ग में मेटाक्लास पाइथन metaclass.__getattr__ विधि को देखता है और देखता है कि यह विशेषता को हल कर सकता है या नहीं।इसे न्यूनतम उदाहरण के साथ चित्रित करने के लिए:

from types import DynamicClassAttribute 

# Metaclass 
class Funny(type): 

    def __getattr__(self, value): 
     print('search in meta') 
     # Normally you would implement here some ifs/elifs or a lookup in a dictionary 
     # but I'll just return the attribute 
     return Funny.dynprop 

    # Metaclasses dynprop: 
    dynprop = 'Meta' 

class Fun(metaclass=Funny): 
    def __init__(self, value): 
     self._dynprop = value 

    @DynamicClassAttribute 
    def dynprop(self): 
     return self._dynprop 

और यहां "गतिशील" भाग आता है। आप वर्ग पर dynprop फोन यदि यह मेटा में खोज और वापस आ जाएगी मेटा के dynprop:

Fun.dynprop 

जो प्रिंट:

search in meta 
'Meta' 

तो हम metaclass.__getattr__ लागू और मूल विशेषता लौटे (जो था नई संपत्ति के समान नाम से परिभाषित)।

उदाहरण के लिए Fun -instance की dynprop दिया जाता है जबकि:

Fun('Not-Meta').dynprop 

हम ओवरराइड विशेषता मिलती है:

'Not-Meta' 

इस से मेरा निष्कर्ष यह है कि DynamicClassAttribute महत्वपूर्ण है अगर आप चाहते हैं मेटाक्लास में उपयोग किए जाने वाले समान नाम के साथ उप-वर्गों को एक विशेषता रखने की अनुमति देने के लिए। आप इसे उदाहरणों पर छाया देंगे लेकिन यदि आप इसे कक्षा में कॉल करते हैं तो यह अभी भी सुलभ है।

मैं पुराने संस्करण में Enum के व्यवहार में चली गई तो मैं इसे यहाँ में छोड़ दिया:

पुराने संस्करण

DynamicClassAttribute सिर्फ उपयोगी है (मैं उस बिंदु पर वास्तव में यकीन नहीं है) करता है, तो आपको संदेह है कि सबक्लस पर सेट की गई विशेषता और बेस-क्लास पर एक संपत्ति के बीच संघर्ष का नाम हो सकता है।

क्योंकि इस metaclasses (पर कैसे वर्ग गुण कहा जाता है एक अच्छा स्पष्टीकरण this blog post में पाया जा सकता) का उपयोग किए बिना काम नहीं करेगा क्योंकि विशेषता देखने थोड़ा अलग है आप, metaclasses के बारे में कम से कम कुछ मूल बातें पता करने की आवश्यकता होगी metaclasses के साथ।

मान लीजिए आपके पास:

class Funny(type): 
    dynprop = 'Very important meta attribute, do not override' 

class Fun(metaclass=Funny): 
    def __init__(self, value): 
     self._stub = value 

    @property 
    def dynprop(self): 
     return 'Haha, overridden it with {}'.format(self._stub) 

और फिर कहते हैं:

Fun.dynprop 

0x1b3d9fd19a8

पर संपत्ति और उदाहरण पर हम पाते हैं:

Fun(2).dynprop 

बुरा 'Haha, यह 2 के साथ अधिरोहित' ... यह खो जाता है। लेकिन प्रतीक्षा करें कि हम metaclass विशेष लुकअप का उपयोग कर सकते हैं: चलो __getattr__ (फ़ॉलबैक) लागू करें और dynprop को DynamicClassAttribute के रूप में कार्यान्वित करें।क्योंकि यह के अनुसार documentation है कि अपने उद्देश्य है - __getattr__ पर वापस आने की अगर यह वर्ग पर कहा जाता है:

Fun.dynprop 

जो प्रिंट:

search in meta 
'Meta' 

from types import DynamicClassAttribute 

class Funny(type): 
    def __getattr__(self, value): 
     print('search in meta') 
     return Funny.dynprop 

    dynprop = 'Meta' 

class Fun(metaclass=Funny): 
    def __init__(self, value): 
     self._dynprop = value 

    @DynamicClassAttribute 
    def dynprop(self): 
     return self._dynprop 

अब हम वर्ग विशेषता का उपयोग

इसलिए हमने metaclass.__getattr__ का आह्वान किया और मूल विशेषता को वापस कर दिया (जिसे नई संपत्ति के समान नाम से परिभाषित किया गया था)।

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

'Not-Meta' 

खैर कि विचार हम उदाहरण बनाकर बिना पहले से परिभाषित लेकिन ओवरराइड विशेषताओं के metaclasses का उपयोग कर कहीं और भेजें कर सकते हैं बहुत बुरा नहीं है:

Fun('Not-Meta').dynprop 

हम ओवरराइड विशेषता मिलता है।

from enum import Enum 

class Fun(Enum): 
    name = 'me' 
    age = 28 
    hair = 'brown' 

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

Fun.name 
# <Fun.name: 'me'> 

लेकिन आप यह भी name विशेषता है कि DynamicClassAttribute के रूप में परिभाषित किया गया था (जो देता है जो नाम चर वास्तव में है) तक पहुँचने की अनुमति देना चाहते:

Fun('me').name 
# 'name' 

क्योंकि अन्यथा आप कैसे 28 के नाम का उपयोग कर सकता है ?

Fun.hair.age 
# <Fun.age: 28> 
# BUT: 
Fun.hair.name 
# returns 'hair' 

अंतर देखें? दूसरा <Fun.name: 'me'> क्यों नहीं लौटाता है? यह DynamicClassAttribute के उपयोग के कारण है। तो आप मूल संपत्ति को छाया कर सकते हैं लेकिन बाद में इसे "रिलीज़" कर सकते हैं। यह व्यवहार मेरे उदाहरण में दिखाया गया है और कम से कम __new__ और __prepare__ के उपयोग की आवश्यकता है। लेकिन इसके लिए आपको यह जानने की ज़रूरत है कि यह वास्तव में कैसे काम करता है और कई ब्लॉगों और स्टैक ओवरफ्लो-उत्तरों में समझाया गया है जो मुझे इससे बेहतर समझा सकता है, इसलिए मैं उस गहराई में जा रहा हूं (और मुझे यकीन नहीं है कि मैं इसे कम क्रम में हल कर सकता हूं)।

वास्तविक उपयोग-मामले विरल लेकिन निश्चित समय एक propably कुछ के बारे में सोच सकते हैं हो सकता है ... DynamicClassAttribute के प्रलेखन पर

बहुत अच्छा चर्चा: "we added it because we needed it"

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