[ टीएल? डीआर आप एक कोड उदाहरण के लिए अंत तक छोड़ सकते हैं ।]
मैं वास्तव में एक अलग मुहावरे का उपयोग करना पसंद करता हूं, जो एक बंद के रूप में उपयोग करने के लिए थोड़ा सा शामिल है, लेकिन यदि आपके पास अधिक जटिल उपयोग का मामला है तो अच्छा है।
पहले थोड़ी पृष्ठभूमि।
गुण इसमें उपयोगी हैं कि वे हमें प्रोग्रामेटिक तरीके से सेटिंग और मान दोनों को संभालने की अनुमति देते हैं, लेकिन फिर भी विशेषताओं को विशेषताओं के रूप में एक्सेस करने की अनुमति देते हैं। हम 'हो जाता है' को 'गणना' (अनिवार्य रूप से) में बदल सकते हैं और हम 'सेट' को 'घटनाओं' में बदल सकते हैं। तो मान लें कि हमारे पास निम्न वर्ग है, जिसे मैंने जावा जैसे गेटर्स और सेटर के साथ कोडित किया है।
class Example(object):
def __init__(self, x=None, y=None):
self.x = x
self.y = y
def getX(self):
return self.x or self.defaultX()
def getY(self):
return self.y or self.defaultY()
def setX(self, x):
self.x = x
def setY(self, y):
self.y = y
def defaultX(self):
return someDefaultComputationForX()
def defaultY(self):
return someDefaultComputationForY()
आप सोच रहे होंगे कि मैंने कॉल क्यों नहीं किया defaultX
और defaultY
वस्तु __init__
विधि में। कारण यह है कि हमारे मामले में मैं यह मान लेना चाहता हूं कि someDefaultComputation
विधियां समय के साथ बदलते मूल्यों को वापस लाती हैं, टाइमस्टैम्प कहती हैं, और जब भी x
(या y
) सेट नहीं किया जाता है (जहां, इस उदाहरण के उद्देश्य के लिए, "सेट नहीं" का अर्थ है "सेट" कुछ नहीं ") मैं डिफ़ॉल्ट गणना का x
's (या y
' s) मान चाहता हूं ।
तो यह ऊपर वर्णित कई कारणों के लिए लंगड़ा है। मैं गुणों का उपयोग करके इसे फिर से लिखूंगा:
class Example(object):
def __init__(self, x=None, y=None):
self._x = x
self._y = y
@property
def x(self):
return self.x or self.defaultX()
@x.setter
def x(self, value):
self._x = value
@property
def y(self):
return self.y or self.defaultY()
@y.setter
def y(self, value):
self._y = value
# default{XY} as before.
हमने क्या हासिल किया है? हमने इन विशेषताओं को विशेषताओं के रूप में संदर्भित करने की क्षमता प्राप्त की है, भले ही दृश्यों के पीछे, हम चल रहे तरीकों को समाप्त करते हैं।
बेशक गुणों की वास्तविक शक्ति यह है कि हम आम तौर पर इन तरीकों को सिर्फ प्राप्त करने और मूल्यों को स्थापित करने के अलावा कुछ करना चाहते हैं (अन्यथा गुणों का उपयोग करने का कोई मतलब नहीं है)। मैंने अपने गेटर उदाहरण में ऐसा किया। जब भी मूल्य निर्धारित नहीं होता है, तो हम मूल रूप से एक फंक्शन बॉडी चला रहे होते हैं। यह एक बहुत ही सामान्य पैटर्न है।
लेकिन हम क्या खो रहे हैं, और हम क्या नहीं कर सकते हैं?
मेरे विचार में, मुख्य झुंझलाहट यह है कि यदि आप एक गटर को परिभाषित करते हैं (जैसा कि हम यहां करते हैं) तो आपको एक सेटर को भी परिभाषित करना होगा। [१] यह अतिरिक्त शोर है जो कोड को बंद कर देता है।
एक और झुंझलाहट यह है कि हमें अभी भी इसमें x
और y
मूल्यों को शुरू करना है __init__
। (ठीक है, निश्चित रूप से हम उनका उपयोग करके जोड़ सकते हैं setattr()
लेकिन यह अतिरिक्त कोड है।)
तीसरा, जावा जैसे उदाहरण के विपरीत, गेटर्स अन्य मापदंडों को स्वीकार नहीं कर सकते हैं। अब मैं आपको पहले ही यह कहते हुए सुन सकता हूँ, अगर यह पैरामीटर ले रहा है तो यह अच्छा नहीं है! एक आधिकारिक अर्थ में, यह सच है। लेकिन व्यावहारिक अर्थ में, कोई कारण नहीं है कि हम एक नामित विशेषता - जैसे x
- और कुछ विशिष्ट मापदंडों के लिए इसके मूल्य को निर्धारित करने में सक्षम नहीं होना चाहिए ।
यह अच्छा होगा अगर हम ऐसा कुछ कर सकते हैं:
e.x[a,b,c] = 10
e.x[d,e,f] = 20
उदाहरण के लिए। निकटतम हम प्राप्त कर सकते हैं कुछ विशेष शब्दार्थों को लागू करने के लिए असाइनमेंट को ओवरराइड करना है:
e.x = [a,b,c,10]
e.x = [d,e,f,30]
और निश्चित रूप से सुनिश्चित करें कि हमारा सेटर जानता है कि पहले तीन मानों को एक शब्दकोश की कुंजी के रूप में कैसे निकाला जाए और इसके मूल्य को एक संख्या या कुछ पर सेट करें।
लेकिन यहां तक कि अगर हमने किया है कि हम अभी भी गुणों के साथ इसका समर्थन नहीं कर सकते हैं, क्योंकि मूल्य प्राप्त करने का कोई तरीका नहीं है क्योंकि हम सभी मापदंडों को प्राप्त करने वाले को पारित नहीं कर सकते हैं। इसलिए हमें एक विषमता का परिचय देते हुए सब कुछ वापस करना पड़ा।
जावा-स्टाइल गेटटर / सेटर हमें इसे संभालने देता है, लेकिन हमें गेट्टर / सेटर की आवश्यकता है।
मेरे मन में जो हम वास्तव में चाहते हैं वह कुछ है जो निम्नलिखित आवश्यकताओं को कैप्चर करता है:
उपयोगकर्ता किसी दिए गए विशेषता के लिए केवल एक विधि को परिभाषित करते हैं और वहां संकेत कर सकते हैं कि क्या विशेषता केवल पढ़ने के लिए है या रीड-राइट है। गुण गुणकारी होने पर यह परीक्षण विफल हो जाता है।
उपयोगकर्ता को फ़ंक्शन को अंतर्निहित अतिरिक्त चर को परिभाषित करने की कोई आवश्यकता नहीं है, इसलिए हमें कोड में __init__
या उसकी आवश्यकता नहीं है setattr
। चर सिर्फ इस तथ्य से मौजूद है कि हमने यह नई शैली की विशेषता बनाई है।
विशेषता के लिए कोई भी डिफ़ॉल्ट कोड विधि बॉडी में ही निष्पादित होता है।
हम विशेषता को एक विशेषता के रूप में सेट कर सकते हैं और इसे एक विशेषता के रूप में संदर्भित कर सकते हैं।
हम विशेषता को पैरामीटर कर सकते हैं।
कोड के संदर्भ में, हम लिखने का एक तरीका चाहते हैं:
def x(self, *args):
return defaultX()
और फिर कर सकेंगे:
print e.x -> The default at time T0
e.x = 1
print e.x -> 1
e.x = None
print e.x -> The default at time T1
इत्यादि।
हम एक पैरामीटर योग्य विशेषता के विशेष मामले के लिए ऐसा करने का एक तरीका भी चाहते हैं, लेकिन फिर भी डिफ़ॉल्ट असाइन केस को काम करने की अनुमति देते हैं। आप देखेंगे कि मैंने इससे कैसे निपटा।
अब टू द पॉइंट (या! द पॉइंट!)। इसके लिए मैं जो समाधान लेकर आया, वह इस प्रकार है।
हम एक संपत्ति की धारणा को बदलने के लिए एक नई वस्तु बनाते हैं। ऑब्जेक्ट का उद्देश्य इसके लिए सेट किए गए वैरिएबल के मूल्य को संग्रहीत करना है, लेकिन यह कोड पर एक हैंडल भी रखता है जो जानता है कि डिफ़ॉल्ट की गणना कैसे करें। इसका काम सेट को स्टोर करना है value
या method
अगर उस मूल्य को सेट नहीं करना है तो उसे चलाना है ।
चलो इसे ए कहते हैं UberProperty
।
class UberProperty(object):
def __init__(self, method):
self.method = method
self.value = None
self.isSet = False
def setValue(self, value):
self.value = value
self.isSet = True
def clearValue(self):
self.value = None
self.isSet = False
मेरा मानना method
है कि एक वर्ग विधि है, value
का मान है UberProperty
, और मैंने जोड़ा है isSet
क्योंकि None
एक वास्तविक मूल्य हो सकता है और यह हमें एक साफ तरीका बताता है कि वास्तव में "कोई मूल्य नहीं" है। एक और तरीका किसी प्रकार का प्रहरी है।
यह मूल रूप से हमें एक वस्तु देता है जो हम जो चाहते हैं वह कर सकते हैं, लेकिन हम वास्तव में इसे अपनी कक्षा में कैसे डालते हैं? खैर, गुण सज्जाकारों का उपयोग करते हैं; हम क्यों नहीं? आइए देखें कि यह कैसा दिख सकता है (यहां से मैं सिर्फ एक 'विशेषता' का उपयोग करने के लिए छड़ी जा रहा हूं, x
)।
class Example(object):
@uberProperty
def x(self):
return defaultX()
यह वास्तव में अभी तक निश्चित रूप से काम नहीं करता है। हमें इसे लागू करना uberProperty
और सुनिश्चित करना है कि यह प्राप्त और सेट दोनों को संभालता है।
चलो के साथ शुरू करते हैं।
मेरा पहला प्रयास बस एक नई UberProperty ऑब्जेक्ट बनाना और उसे वापस करना था:
def uberProperty(f):
return UberProperty(f)
मुझे जल्दी से पता चला, निश्चित रूप से, कि यह काम नहीं करता है: पायथन कभी भी कॉल करने योग्य वस्तु को बांधता नहीं है और मुझे फ़ंक्शन को कॉल करने के लिए ऑब्जेक्ट की आवश्यकता होती है। कक्षा में डेकोरेटर बनाने से भी काम नहीं चलता, क्योंकि अब हमारे पास क्लास है, फिर भी हमारे पास काम करने के लिए कोई वस्तु नहीं है।
तो हम यहाँ और अधिक करने में सक्षम होने की जरूरत है। हम जानते हैं कि एक विधि को केवल एक समय का प्रतिनिधित्व करने की आवश्यकता है, इसलिए चलो आगे बढ़ें और हमारे डेकोरेटर को रखें, लेकिन UberProperty
केवल method
संदर्भ को संग्रहीत करने के लिए संशोधित करें :
class UberProperty(object):
def __init__(self, method):
self.method = method
यह कॉल करने योग्य भी नहीं है, इसलिए फिलहाल कुछ भी काम नहीं कर रहा है।
हम तस्वीर को कैसे पूरा करते हैं? जब हम अपने नए डेकोरेटर का उपयोग करके उदाहरण वर्ग बनाते हैं, तो हम क्या करते हैं:
class Example(object):
@uberProperty
def x(self):
return defaultX()
print Example.x <__main__.UberProperty object at 0x10e1fb8d0>
print Example().x <__main__.UberProperty object at 0x10e1fb8d0>
दोनों ही मामलों में हम वापस आ जाते हैं UberProperty
जो निश्चित रूप से एक कॉल करने योग्य नहीं है, इसलिए यह बहुत काम का नहीं है।
जिस चीज की हमें जरूरत है UberProperty
, उसे डेकोरेटर द्वारा बनाए गए इंस्टेंस को क्लास में किसी ऑब्जेक्ट के लिए बनाए जाने के बाद डायनामिक तरीके से बांधने का कुछ तरीका है , इससे पहले कि यूजर्स को उस यूजर को यूज के लिए वापस कर दिया गया है। उम, हाँ, यह एक __init__
कॉल है, यार।
आइए लिखते हैं कि हम क्या चाहते हैं कि हमारा परिणाम पहले मिले। हम UberProperty
एक उदाहरण के लिए बाध्य कर रहे हैं , इसलिए लौटने के लिए एक स्पष्ट बात एक बाउंडअपप्रोपरेटी होगी। यह वह जगह है जहाँ हम वास्तव में x
विशेषता के लिए स्थिति बनाए रखेंगे ।
class BoundUberProperty(object):
def __init__(self, obj, uberProperty):
self.obj = obj
self.uberProperty = uberProperty
self.isSet = False
def setValue(self, value):
self.value = value
self.isSet = True
def getValue(self):
return self.value if self.isSet else self.uberProperty.method(self.obj)
def clearValue(self):
del self.value
self.isSet = False
अब हम प्रतिनिधित्व करते हैं; किसी वस्तु पर ये कैसे प्राप्त होते हैं? कुछ दृष्टिकोण हैं, लेकिन सबसे आसान यह समझाने __init__
के लिए कि मानचित्रण करने के लिए विधि का उपयोग करता है । जब तक __init__
हमारे डेकोरेटर चलाए जाते हैं तब तक कहा जाता है, इसलिए केवल ऑब्जेक्ट के माध्यम से देखने __dict__
और किसी भी विशेषता को अपडेट करने की आवश्यकता है जहां विशेषता का मूल्य प्रकार का है UberProperty
।
अब, उबेर-गुण शांत हैं और हम शायद उन्हें बहुत उपयोग करना चाहते हैं, इसलिए यह सिर्फ एक आधार वर्ग बनाने के लिए समझ में आता है जो सभी उपवर्गों के लिए ऐसा करता है। मुझे लगता है कि आप जानते हैं कि बेस क्लास को क्या कहा जाने वाला है।
class UberObject(object):
def __init__(self):
for k in dir(self):
v = getattr(self, k)
if isinstance(v, UberProperty):
v = BoundUberProperty(self, v)
setattr(self, k, v)
हम इसे जोड़ते हैं, हमारे उदाहरण को इनहेरिट से बदलते हैं UberObject
, और ...
e = Example()
print e.x -> <__main__.BoundUberProperty object at 0x104604c90>
संशोधित होने के बाद x
:
@uberProperty
def x(self):
return *datetime.datetime.now()*
हम एक साधारण परीक्षण चला सकते हैं:
print e.x.getValue()
print e.x.getValue()
e.x.setValue(datetime.date(2013, 5, 31))
print e.x.getValue()
e.x.clearValue()
print e.x.getValue()
और हमें वह आउटपुट मिलता है जो हम चाहते थे:
2013-05-31 00:05:13.985813
2013-05-31 00:05:13.986290
2013-05-31
2013-05-31 00:05:13.986310
(जी, मैं देर से काम कर रहा हूं।)
ध्यान दें कि मैं का इस्तेमाल किया है getValue
, setValue
और clearValue
यहाँ। इसका कारण यह है कि मैंने अभी तक इन्हें स्वचालित रूप से वापस लाने के साधनों में लिंक नहीं किया है।
लेकिन मुझे लगता है कि यह अब के लिए रुकने के लिए एक अच्छी जगह है, क्योंकि मैं थक गया हूं। आप यह भी देख सकते हैं कि जो मुख्य कार्यक्षमता हम चाहते थे, वह जगह पर है; बाकी विंडो ड्रेसिंग है। महत्वपूर्ण प्रयोज्यता विंडो ड्रेसिंग, लेकिन वह तब तक प्रतीक्षा कर सकती है जब तक कि मेरे पास पोस्ट को अपडेट करने के लिए कोई बदलाव न हो।
मैं इन बातों को संबोधित करके अगली पोस्टिंग में उदाहरण समाप्त करूँगा:
हमें यह सुनिश्चित करने की आवश्यकता है कि UberObject's __init__
को हमेशा उपवर्गों द्वारा बुलाया जाता है।
- इसलिए हम या तो इसे कहीं बुलाते हैं या हम इसे लागू होने से रोकते हैं।
- हम देखेंगे कि मेटाक्लास के साथ यह कैसे किया जाता है।
हमें यह सुनिश्चित करने की ज़रूरत है कि हम उस सामान्य मामले को संभालें जहाँ कोई 'उपनाम' किसी अन्य चीज़ के लिए कार्य करता है, जैसे:
class Example(object):
@uberProperty
def x(self):
...
y = x
हमें डिफ़ॉल्ट रूप e.x
से वापस लौटने की आवश्यकता है e.x.getValue()
।
- जो हम वास्तव में देखेंगे वह यह है कि यह एक ऐसा क्षेत्र है जहां मॉडल विफल हो जाता है।
- यह पता चला है कि मूल्य प्राप्त करने के लिए हमें हमेशा फ़ंक्शन कॉल का उपयोग करना होगा।
- लेकिन हम इसे एक नियमित फ़ंक्शन कॉल की तरह बना सकते हैं और उपयोग करने से बच सकते हैं
e.x.getValue()
। (ऐसा करना स्पष्ट है, यदि आपने इसे पहले से तय नहीं किया है।)
हमें सेटिंग का समर्थन करने की आवश्यकता है e.x directly
, जैसे कि e.x = <newvalue>
। हम इसे मूल श्रेणी में भी कर सकते हैं, लेकिन हमें इसे __init__
संभालने के लिए अपना कोड अपडेट करना होगा।
अंत में, हम पैरामीटरयुक्त विशेषताएँ जोड़ेंगे। यह बहुत स्पष्ट होना चाहिए कि हम ऐसा कैसे करेंगे।
यहाँ कोड है क्योंकि यह अब तक मौजूद है:
import datetime
class UberObject(object):
def uberSetter(self, value):
print 'setting'
def uberGetter(self):
return self
def __init__(self):
for k in dir(self):
v = getattr(self, k)
if isinstance(v, UberProperty):
v = BoundUberProperty(self, v)
setattr(self, k, v)
class UberProperty(object):
def __init__(self, method):
self.method = method
class BoundUberProperty(object):
def __init__(self, obj, uberProperty):
self.obj = obj
self.uberProperty = uberProperty
self.isSet = False
def setValue(self, value):
self.value = value
self.isSet = True
def getValue(self):
return self.value if self.isSet else self.uberProperty.method(self.obj)
def clearValue(self):
del self.value
self.isSet = False
def uberProperty(f):
return UberProperty(f)
class Example(UberObject):
@uberProperty
def x(self):
return datetime.datetime.now()
[१] मैं इस मामले में अभी भी पीछे रह सकता हूँ।