मैं यह समझने की कोशिश कर रहा हूं कि पायथन के वर्णनकर्ता क्या हैं और वे किसके लिए उपयोगी हो सकते हैं।
निम्नलिखित विशेष विधियों में से किसी एक के साथ वर्णक वर्ग गुण (जैसे गुण या विधियाँ) हैं:
__get__ (गैर-डेटा विवरणक विधि, उदाहरण के लिए किसी विधि / फ़ंक्शन पर)
__set__ (डेटा डिस्क्रिप्टर विधि, उदाहरण के लिए एक संपत्ति उदाहरण पर)
__delete__ (डेटा विवरणक विधि)
इन डिस्क्रिप्टर ऑब्जेक्ट्स का उपयोग अन्य ऑब्जेक्ट क्लास परिभाषाओं पर विशेषताओं के रूप में किया जा सकता है। (अर्थात्, वे __dict__वर्ग वस्तु में रहते हैं।)
डिस्क्रिप्टिव ऑब्जेक्ट्स का उपयोग प्रोग्रामेटिक रूप से बिंदीदार लुकअप (जैसे foo.descriptor) के परिणाम को सामान्य अभिव्यक्ति, असाइनमेंट और यहां तक कि डिलीशन के प्रबंधन के लिए किया जा सकता है ।
कार्य / तरीकों, बाध्य तरीकों, property, classmethod, और staticmethodसभी उपयोग को नियंत्रित करने के इन विशेष तरीकों कि वे किस तरह बिंदीदार देखने के माध्यम से पहुंचा जा सकता है।
एक डेटा डिस्क्रिप्टर , जैसे property, ऑब्जेक्ट की एक सरल स्थिति के आधार पर विशेषताओं के आलसी मूल्यांकन के लिए अनुमति दे सकता है, यदि आप प्रत्येक संभावित विशेषता को कम से कम मेमोरी का उपयोग करने के उदाहरणों की अनुमति देते हैं।
एक अन्य डेटा डिस्क्रिप्टर, एक member_descriptor, जिसके द्वारा बनाया गया है __slots__, वर्ग को अधिक लचीले लेकिन अंतरिक्ष-उपभोग के बजाय एक उत्परिवर्तित टपल-जैसे डेटास्ट्रक्चर में डेटा को स्टोर करने की अनुमति देकर मेमोरी सेविंग की अनुमति देता है __dict__।
गैर-डेटा डिस्क्रिप्टर, आमतौर पर उदाहरण, वर्ग और स्थिर तरीकों से, उनके गैर-डेटा डिस्क्रिप्टर विधि से उनके निहित पहले तर्क (आमतौर पर नाम clsऔर selfक्रमशः) प्राप्त होते हैं __get__।
पायथन के अधिकांश उपयोगकर्ताओं को केवल सरल उपयोग सीखने की आवश्यकता है, और आगे वर्णनकर्ताओं के कार्यान्वयन को सीखने या समझने की कोई आवश्यकता नहीं है।
गहराई में: डिस्क्रिप्टर्स क्या हैं?
एक डिस्क्रिप्टर निम्नलिखित विधियों में से किसी के साथ एक वस्तु है ( __get__, __set__या __delete__,), जिसका उद्देश्य डॉटेड-लुकअप के माध्यम से उपयोग किया जाना है जैसे कि यह एक उदाहरण का विशिष्ट गुण है। एक स्वामी-वस्तु के लिए, obj_instanceएक descriptorवस्तु के साथ :
obj_instance.descriptorका आह्वान
descriptor.__get__(self, obj_instance, owner_class)वापस लौटने वाली value
यह कैसे सभी तरीकों और getएक संपत्ति काम पर।
obj_instance.descriptor = valueइनवॉइस
descriptor.__set__(self, obj_instance, value)लौटना None
यह setterएक संपत्ति पर काम करता है।
del obj_instance.descriptorइनवॉइस
descriptor.__delete__(self, obj_instance)लौटना None
यह deleterएक संपत्ति पर काम करता है।
obj_instanceवह उदाहरण है जिसका वर्ग वर्णनात्मक वस्तु का उदाहरण समाहित करता है। वर्णनकर्ताself का उदाहरण है (शायद कक्षा के लिए सिर्फ एक )obj_instance
इसे कोड के साथ परिभाषित करने के लिए, एक ऑब्जेक्ट एक डिस्क्रिप्टर है यदि इसकी विशेषताओं का सेट आवश्यक विशेषताओं में से किसी के साथ प्रतिच्छेद करता है:
def has_descriptor_attrs(obj):
return set(['__get__', '__set__', '__delete__']).intersection(dir(obj))
def is_descriptor(obj):
"""obj can be instance of descriptor or the descriptor class"""
return bool(has_descriptor_attrs(obj))
एक डेटा डिस्क्रिप्टर में एक __set__और / या होता है __delete__।
एक गैर-डेटा-डिस्क्रिप्टर में न तो है __set__और न ही __delete__।
def has_data_descriptor_attrs(obj):
return set(['__set__', '__delete__']) & set(dir(obj))
def is_data_descriptor(obj):
return bool(has_data_descriptor_attrs(obj))
बिल्टिन डिसिप्लिनर ऑब्जेक्ट उदाहरण:
classmethod
staticmethod
property
- सामान्य रूप से कार्य करता है
गैर-डेटा डिस्क्रिप्टर्स
हम देख सकते हैं कि classmethodऔर staticmethodगैर-डेटा-डिस्क्रिप्टर्स हैं:
>>> is_descriptor(classmethod), is_data_descriptor(classmethod)
(True, False)
>>> is_descriptor(staticmethod), is_data_descriptor(staticmethod)
(True, False)
दोनों में केवल __get__विधि है:
>>> has_descriptor_attrs(classmethod), has_descriptor_attrs(staticmethod)
(set(['__get__']), set(['__get__']))
ध्यान दें कि सभी कार्य गैर-डेटा-डिस्क्रिप्टर्स भी हैं:
>>> def foo(): pass
...
>>> is_descriptor(foo), is_data_descriptor(foo)
(True, False)
डेटा डिस्क्रिप्टर, property
हालाँकि, propertyएक डेटा-डिस्क्रिप्टर है:
>>> is_data_descriptor(property)
True
>>> has_descriptor_attrs(property)
set(['__set__', '__get__', '__delete__'])
बिंदीदार लुकअप ऑर्डर
ये महत्वपूर्ण अंतर हैं , क्योंकि वे एक बिंदीदार देखने के लिए लुकअप ऑर्डर को प्रभावित करते हैं।
obj_instance.attribute
- सबसे पहले यह देखने के लिए कि क्या विशेषता उदाहरण के वर्ग पर एक डेटा-डिस्क्रिप्टर है,
- यदि नहीं, तो यह देखना है कि क्या विशेषता
obj_instanceएस में है __dict__, तो
- यह अंत में एक गैर-डेटा-डिस्क्रिप्टर पर वापस आ जाता है।
इस लुकअप ऑर्डर का परिणाम यह है कि गैर-डेटा-डिस्क्रिप्टर्स जैसे फ़ंक्शन / तरीके इंस्टेंसेस द्वारा ओवरराइड किए जा सकते हैं ।
रीकैप और नेक्स्ट स्टेप्स
हमने सीखा है कि वर्णनकर्ता से किसी के साथ वस्तुओं रहे हैं __get__, __set__या __delete__। इन डिस्क्रिप्टर ऑब्जेक्ट्स का उपयोग अन्य ऑब्जेक्ट क्लास परिभाषाओं पर विशेषताओं के रूप में किया जा सकता है। अब हम उदाहरण के रूप में आपके कोड का उपयोग करते हुए, उनका उपयोग कैसे करते हैं, इस पर गौर करेंगे।
प्रश्न से कोड का विश्लेषण
यहां आपका कोड है, इसके बाद आपके प्रश्न और प्रत्येक के उत्तर:
class Celsius(object):
def __init__(self, value=0.0):
self.value = float(value)
def __get__(self, instance, owner):
return self.value
def __set__(self, instance, value):
self.value = float(value)
class Temperature(object):
celsius = Celsius()
- मुझे डिस्क्रिप्टर क्लास की आवश्यकता क्यों है?
आपका वर्णनकर्ता यह सुनिश्चित करता है कि आपके पास हमेशा इस श्रेणी की विशेषता के लिए एक फ़्लोट है Temperature, और आप delउस विशेषता को हटाने के लिए उपयोग नहीं कर सकते हैं :
>>> t1 = Temperature()
>>> del t1.celsius
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: __delete__
अन्यथा, आपके विवरणकर्ता स्वामी के वर्ग-वर्ग और उदाहरणों को अनदेखा करते हैं, इसके बजाय, विवरणक में स्थिति संग्रहीत करते हैं। आप एक सरल वर्ग विशेषता के साथ सभी उदाहरणों में आसानी से राज्य साझा कर सकते हैं (इसलिए जब तक आप इसे हमेशा कक्षा के लिए एक फ़्लोट के रूप में सेट करते हैं और इसे कभी नहीं हटाते हैं, या ऐसा करने वाले आपके कोड के उपयोगकर्ताओं के साथ सहज हैं):
class Temperature(object):
celsius = 0.0
यह आपके उदाहरण के समान व्यवहार करता है (नीचे दिए गए प्रश्न 3 की प्रतिक्रिया देखें), लेकिन पायथन बिलिन ( property) का उपयोग करता है , और इसे अधिक मुहावरेदार माना जाएगा:
class Temperature(object):
_celsius = 0.0
@property
def celsius(self):
return type(self)._celsius
@celsius.setter
def celsius(self, value):
type(self)._celsius = float(value)
- यहाँ उदाहरण और मालिक क्या है? ( पाने में )। इन मापदंडों का उद्देश्य क्या है?
instanceवर्णनकर्ता को कॉल करने वाले स्वामी का उदाहरण है। स्वामी वह वर्ग है जिसमें डेटा पॉइंट तक पहुंच का प्रबंधन करने के लिए डिस्क्रिप्टर ऑब्जेक्ट का उपयोग किया जाता है। अधिक वर्णनात्मक चर नामों के लिए इस उत्तर के पहले पैराग्राफ के बगल में वर्णनकर्ताओं को परिभाषित करने वाले विशेष तरीकों के विवरण देखें।
- मैं इस उदाहरण को कैसे कॉल / उपयोग करूंगा?
यहाँ एक प्रदर्शन है:
>>> t1 = Temperature()
>>> t1.celsius
0.0
>>> t1.celsius = 1
>>>
>>> t1.celsius
1.0
>>> t2 = Temperature()
>>> t2.celsius
1.0
आप विशेषता नहीं हटा सकते:
>>> del t2.celsius
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: __delete__
और आप एक चर को निर्दिष्ट नहीं कर सकते हैं जिसे फ्लोट में नहीं बदला जा सकता है:
>>> t1.celsius = '0x02'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in __set__
ValueError: invalid literal for float(): 0x02
अन्यथा, आपके पास यहां जो कुछ भी है वह सभी उदाहरणों के लिए एक वैश्विक स्थिति है, जिसे किसी भी उदाहरण को निर्दिष्ट करके प्रबंधित किया जाता है।
यह उम्मीद है कि सबसे अनुभवी पायथन प्रोग्रामर इस परिणाम को पूरा करेंगे property, डेकोरेटर का उपयोग करना होगा , जो हुड के नीचे एक ही विवरणक का उपयोग करता है, लेकिन व्यवहार को मालिक वर्ग के कार्यान्वयन में लाता है (फिर से, जैसा कि ऊपर परिभाषित किया गया है):
class Temperature(object):
_celsius = 0.0
@property
def celsius(self):
return type(self)._celsius
@celsius.setter
def celsius(self, value):
type(self)._celsius = float(value)
जिसमें कोड के मूल टुकड़े का सटीक अपेक्षित व्यवहार है:
>>> t1 = Temperature()
>>> t2 = Temperature()
>>> t1.celsius
0.0
>>> t1.celsius = 1.0
>>> t2.celsius
1.0
>>> del t1.celsius
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: can't delete attribute
>>> t1.celsius = '0x02'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 8, in celsius
ValueError: invalid literal for float(): 0x02
निष्कर्ष
हमने उन विशेषताओं को कवर किया है जो डिस्क्रिप्टर को परिभाषित करते हैं, डेटा- और गैर-डेटा-डिस्क्रिप्टर के बीच का अंतर, निर्मित वस्तुएं जो उनका उपयोग करती हैं, और उपयोग के बारे में विशिष्ट प्रश्न।
तो फिर, आप प्रश्न के उदाहरण का उपयोग कैसे करेंगे? मुझे आशा है कि आप नहीं करेंगे। मुझे आशा है कि आप मेरे पहले सुझाव (एक साधारण वर्ग विशेषता) के साथ शुरू करेंगे और यदि आपको लगता है कि यह आवश्यक है तो दूसरे सुझाव (संपत्ति सज्जाकार) पर आगे बढ़ेंगे।
selfऔरinstance?