गेटर्स और सेटर का उपयोग करने के लिए पायथोनिक तरीका क्या है?


340

मैं इसे पसंद कर रहा हूं:

def set_property(property,value):  
def get_property(property):  

या

object.property = value  
value = object.property

मैं पायथन के लिए नया हूं, इसलिए मैं अभी भी वाक्य रचना की खोज कर रहा हूं, और मुझे ऐसा करने के बारे में कुछ सलाह चाहिए।

जवाबों:


684

इसे आजमाएँ: पायथन प्रॉपर्टी

नमूना कोड है:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        print("getter of x called")
        return self._x

    @x.setter
    def x(self, value):
        print("setter of x called")
        self._x = value

    @x.deleter
    def x(self):
        print("deleter of x called")
        del self._x


c = C()
c.x = 'foo'  # setter called
foo = c.x    # getter called
del c.x      # deleter called

2
क्या x के लिए सेटर को इनिशियलाइज़र में बुलाया जाता है जब _x को तत्काल किया जाता है?
केसी

7
@ कैसी: नहीं ._x(जो एक संपत्ति नहीं है, बस एक सादा विशेषता है) के संदर्भ में propertyरैपिंग को दरकिनार कर दिया जाता है। केवल संदर्भ के .xमाध्यम से जाने के लिए property
शैडो रेंजर

277

गेटर्स और सेटर का उपयोग करने के लिए पायथोनिक तरीका क्या है?

"पायथोनिक" तरीका "गेटर्स" और "सेटलर्स" का उपयोग नहीं करना है, बल्कि सादे विशेषताओं का उपयोग करना है, जैसे प्रश्न प्रदर्शित करता है, और delहटाने के लिए (लेकिन नाम निर्दोष ... बिल्डिंस की सुरक्षा के लिए बदल दिए जाते हैं):

value = 'something'

obj.attribute = value  
value = obj.attribute
del obj.attribute

यदि बाद में, आप सेटिंग को बदलना और प्राप्त करना चाहते हैं, तो आप propertyडेकोरेटर का उपयोग करके उपयोगकर्ता कोड में बदलाव किए बिना ऐसा कर सकते हैं :

class Obj:
    """property demo"""
    #
    @property            # first decorate the getter method
    def attribute(self): # This getter method name is *the* name
        return self._attribute
    #
    @attribute.setter    # the property decorates with `.setter` now
    def attribute(self, value):   # name, e.g. "attribute", is the same
        self._attribute = value   # the "value" name isn't special
    #
    @attribute.deleter     # decorate with `.deleter`
    def attribute(self):   # again, the method name is the same
        del self._attribute

(प्रत्येक डेकोरेटर उपयोग की गई कॉपी और पूर्व की प्रॉपर्टी ऑब्जेक्ट को अपडेट करता है, इसलिए ध्यान दें कि आपको प्रत्येक सेट, फ़ंक्शन और विधि को एक ही नाम का उपयोग करना चाहिए।

उपरोक्त परिभाषित करने के बाद, मूल सेटिंग, कोड प्राप्त करना और हटाना एक समान है:

obj = Obj()
obj.attribute = value  
the_value = obj.attribute
del obj.attribute

आपको इससे बचना चाहिए:

def set_property(property,value):  
def get_property(property):  

सबसे पहले, ऊपर काम नहीं करता है, क्योंकि आप उस उदाहरण के लिए एक तर्क प्रदान नहीं करते हैं जो संपत्ति को (आमतौर पर self) सेट किया जाएगा, जो होगा:

class Obj:

    def set_property(self, property, value): # don't do this
        ...
    def get_property(self, property):        # don't do this either
        ...

दूसरे, यह दो विशेष तरीकों के उद्देश्य को दोहराता है, __setattr__और __getattr__

तीसरा, हमारे पास setattrऔर getattrअंतर्निहित कार्य भी हैं ।

setattr(object, 'property_name', value)
getattr(object, 'property_name', default_value)  # default is optional

@propertyडेकोरेटर getters और setters बनाने के लिए है।

उदाहरण के लिए, हम सेटिंग व्यवहार को संशोधित कर सकते हैं ताकि निर्धारित मूल्य पर प्रतिबंध लगाया जा सके:

class Protective(object):

    @property
    def protected_value(self):
        return self._protected_value

    @protected_value.setter
    def protected_value(self, value):
        if acceptable(value): # e.g. type or range check
            self._protected_value = value

सामान्य तौर पर, हम उपयोग करने से बचना चाहते हैं propertyऔर बस प्रत्यक्ष विशेषताओं का उपयोग करते हैं।

यह अजगर के उपयोगकर्ताओं द्वारा अपेक्षित है। कम से कम-आश्चर्य के नियम के बाद, आपको अपने उपयोगकर्ताओं को वे देने की कोशिश करनी चाहिए जब तक वे इसके विपरीत एक बहुत ही आकर्षक कारण न हों।

प्रदर्शन

उदाहरण के लिए, कहें कि हमें 0 और 100 समावेशी के बीच पूर्णांक बनने के लिए हमारी वस्तु की संरक्षित विशेषता की आवश्यकता है, और इसके उचित उपयोग के उपयोगकर्ता को सूचित करने के लिए उपयुक्त संदेशों के साथ इसके विलोपन को रोकें:

class Protective(object):
    """protected property demo"""
    #
    def __init__(self, start_protected_value=0):
        self.protected_value = start_protected_value
    # 
    @property
    def protected_value(self):
        return self._protected_value
    #
    @protected_value.setter
    def protected_value(self, value):
        if value != int(value):
            raise TypeError("protected_value must be an integer")
        if 0 <= value <= 100:
            self._protected_value = int(value)
        else:
            raise ValueError("protected_value must be " +
                             "between 0 and 100 inclusive")
    #
    @protected_value.deleter
    def protected_value(self):
        raise AttributeError("do not delete, protected_value can be set to 0")

(ध्यान दें कि जो संपत्ति के तरीकों __init__को संदर्भित करता है self.protected_valueलेकिन यह संदर्भित करता है self._protected_value। यह ऐसा है जो __init__सार्वजनिक एपीआई के माध्यम से संपत्ति का उपयोग करता है, यह सुनिश्चित करता है कि यह "संरक्षित" है।)

और उपयोग:

>>> p1 = Protective(3)
>>> p1.protected_value
3
>>> p1 = Protective(5.0)
>>> p1.protected_value
5
>>> p2 = Protective(-5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
  File "<stdin>", line 15, in protected_value
ValueError: protectected_value must be between 0 and 100 inclusive
>>> p1.protected_value = 7.3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 17, in protected_value
TypeError: protected_value must be an integer
>>> p1.protected_value = 101
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 15, in protected_value
ValueError: protectected_value must be between 0 and 100 inclusive
>>> del p1.protected_value
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 18, in protected_value
AttributeError: do not delete, protected_value can be set to 0

क्या नाम मायने रखते हैं?

हाँ, वे करते हैं.setterऔर .deleterमूल संपत्ति की प्रतियां बनाएं। यह उपवर्गों को माता-पिता के व्यवहार में बदलाव किए बिना व्यवहार को ठीक से संशोधित करने की अनुमति देता है।

class Obj:
    """property demo"""
    #
    @property
    def get_only(self):
        return self._attribute
    #
    @get_only.setter
    def get_or_set(self, value):
        self._attribute = value
    #
    @get_or_set.deleter
    def get_set_or_delete(self):
        del self._attribute

अब इसे काम करने के लिए, आपको संबंधित नामों का उपयोग करना होगा:

obj = Obj()
# obj.get_only = 'value' # would error
obj.get_or_set = 'value'  
obj.get_set_or_delete = 'new value'
the_value = obj.get_only
del obj.get_set_or_delete
# del obj.get_or_set # would error

मुझे यकीन नहीं है कि यह कहाँ उपयोगी होगा, लेकिन उपयोग-मामला है, यदि आप केवल एक संपत्ति, सेट और / या हटाना चाहते हैं। संभवतः सबसे अच्छा एक ही नाम वाले शब्दार्थ संपत्ति से चिपके रहते हैं।

निष्कर्ष

सरल विशेषताओं के साथ शुरू करें।

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

नामित कार्यों से बचें set_...और get_...यही गुण हैं।


उपलब्ध कार्यक्षमता की नकल करने के अलावा, अपने स्वयं के बसने वालों और गेटर्स को लिखने से क्यों बचना चाहिए? मैं समझता हूं कि यह पाइथोनिक तरीका नहीं हो सकता है, लेकिन क्या वास्तव में गंभीर मुद्दे हैं अन्यथा किसी का सामना हो सकता है?
user1350191

4
आपके डेमो में, __init__विधि संदर्भित करता है self.protected_valueलेकिन गेटटर और सेटर को संदर्भित करता है self._protected_value। क्या आप बता सकते हैं कि यह कैसे काम करता है? मैंने आपके कोड का परीक्षण किया और यह काम करता है - तो यह एक टाइपो नहीं है।
कोडफ़ेस्टर

2
@codeforester मैं पहले मेरे जवाब में जवाब देने की उम्मीद कर रहा था, लेकिन जब तक मैं कर सकता हूं, यह टिप्पणी पर्याप्त होनी चाहिए। मुझे उम्मीद है कि आप सार्वजनिक एपी के माध्यम से संपत्ति का उपयोग कर सकते हैं, यह सुनिश्चित कर सकते हैं कि यह "संरक्षित" है। यह एक संपत्ति के साथ इसे "सुरक्षित" करने के लिए समझ में नहीं आता है और फिर इसके बजाय गैर-सार्वजनिक एपीआई का उपयोग __init__करेगा?
हारून हॉल

2
हां, @AaronHall अब मिल गया। मुझे एहसास नहीं था self.protected_value = start_protected_valueकि वास्तव में सेटर फ़ंक्शन को कॉल कर रहा है; मुझे लगा कि यह एक असाइनमेंट है।
कोडवर्ड

1
imho यह स्वीकृत उत्तर होना चाहिए, अगर मुझे सही ढंग से समझ में आया कि उदाहरण के लिए जावा की तुलना में अजगर केवल विपरीत बिंदु लेता है। डिफ़ॉल्ट रूप से सब कुछ निजी बनाने और कुछ अतिरिक्त कोड लिखने के बजाय जब इसे सार्वजनिक रूप से अजगर की जरूरत हो तो आप सब कुछ सार्वजनिक कर सकते हैं और बाद में गोपनीयता जोड़ सकते हैं
idclev 463035818

27
In [1]: class test(object):
    def __init__(self):
        self.pants = 'pants'
    @property
    def p(self):
        return self.pants
    @p.setter
    def p(self, value):
        self.pants = value * 2
   ....: 
In [2]: t = test()
In [3]: t.p
Out[3]: 'pants'
In [4]: t.p = 10
In [5]: t.p
Out[5]: 20

17

उपयोग करना @propertyऔर @attribute.setterआपको न केवल "पाइथोनिक" तरीके का उपयोग करने में मदद करता है, बल्कि ऑब्जेक्ट बनाते समय और इसे बदलते समय दोनों की विशेषताओं की वैधता की जांच करने के लिए भी।

class Person(object):
    def __init__(self, p_name=None):
        self.name = p_name

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, new_name):
        if type(new_name) == str: #type checking for name property
            self._name = new_name
        else:
            raise Exception("Invalid value for name")

इसके द्वारा, आप वास्तव में _nameक्लाइंट डेवलपर्स से विशेषता छिपाते हैं और नाम संपत्ति प्रकार पर चेक भी करते हैं। ध्यान दें कि दीक्षा के दौरान भी इस दृष्टिकोण का पालन करते हुए सेटर को बुलाया जाता है। इसलिए:

p = Person(12)

को बढ़ावा मिलेगा:

Exception: Invalid value for name

परंतु:

>>>p = person('Mike')
>>>print(p.name)
Mike
>>>p.name = 'George'
>>>print(p.name)
George
>>>p.name = 2.3 # Causes an exception

16

@propertyडेकोरेटर की जाँच करें ।


34
यह बहुत ही एक लिंक केवल जवाब है।
आरोन हॉल


7
यह पूरा उत्तर कैसे है? एक लिंक एक जवाब नहीं है।
एंडी_अ And̷̷̷̷̷y

मुझे लगता है कि यह एक अच्छा जवाब है, क्योंकि दस्तावेज़ में स्पष्ट रूप से कहा गया है कि इसका उपयोग कैसे किया जाए (और यदि पायथन कार्यान्वयन में बदलाव होता है, तो यह चालू रहेगा, और यह ओपी को उस विधि को निर्देशित करता है जो उत्तर-निर्धारण का सुझाव दे रहा है। @ Jean-FrançoisCorletett ने स्पष्ट रूप से कहा है। 'कैसे' यह एक पूर्ण उत्तर है।
हनीबियर

किसी भी स्थिति में, यह उत्तर अन्य उत्तरों के लिए उपयोगी कुछ भी नहीं जोड़ता है और आदर्श रूप से हटा दिया जाना चाहिए।
जॉर्जी

5

आप एक्सेसर्स / म्यूटेटर (यानी @attr.setterऔर @property) का उपयोग कर सकते हैं या नहीं, लेकिन सबसे महत्वपूर्ण बात यह है कि सुसंगत होना चाहिए!

यदि आप @propertyकिसी विशेषता का उपयोग करने के लिए उपयोग कर रहे हैं , जैसे

class myClass:
    def __init__(a):
        self._a = a

    @property
    def a(self):
        return self._a

हर * विशेषता का उपयोग करने के लिए इसका इस्तेमाल करते हैं ! एक एक्सेसर के बिना @propertyकुछ अन्य गुणों को सार्वजनिक रूप से उपयोग करना और कुछ अन्य गुणों को सार्वजनिक रूप से छोड़ना (यानी अंडरस्कोर के बिना नाम) को छोड़ना एक बुरा अभ्यास होगा , जैसे कि ऐसा न करें

class myClass:
    def __init__(a, b):
        self.a = a
        self.b = b

    @property
    def a(self):
        return self.a

ध्यान दें कि self.bसार्वजनिक होने के बावजूद यहां एक स्पष्ट एक्सेसर नहीं है।

इसी तरह बसने वालों (या उत्परिवर्ती ) के साथ, बेझिझक उपयोग करें @attribute.setterलेकिन सुसंगत रहें! जब आप उदा

class myClass:
    def __init__(a, b):
        self.a = a
        self.b = b 

    @a.setter
    def a(self, value):
        return self.a = value

आपके इरादे का अनुमान लगाना मेरे लिए कठिन है। एक तरफ आप कह रहे हैं कि दोनों aऔर bसार्वजनिक कर रहे हैं (उनके नाम में कोई प्रमुख अंडरस्कोर) तो मैं सैद्धांतिक रूप से पहुँच / मे बदलें (प्राप्त / सेट) दोनों के लिए अनुमति दी जानी चाहिए। लेकिन फिर आप केवल एक स्पष्ट म्यूटेटर निर्दिष्ट करते हैं a, जो मुझे बताता है कि शायद मुझे सेट करने में सक्षम नहीं होना चाहिए b। चूंकि आपने एक स्पष्ट उत्परिवर्ती प्रदान किया है, इसलिए मुझे यकीन नहीं है कि अगर स्पष्ट अभिगमकर्ता की कमी ( @property) का अर्थ है कि मुझे उन चर में से किसी का उपयोग करने में सक्षम नहीं होना चाहिए या आप बस उपयोग करने में मितव्ययी हो रहे थे @property

* अपवाद तब होता है जब आप स्पष्ट रूप से कुछ चर को सुलभ या परिवर्तनशील बनाना चाहते हैं लेकिन दोनों नहीं या आप किसी विशेषता तक पहुँचने या परिवर्तन करने के दौरान कुछ अतिरिक्त तर्क करना चाहते हैं। यह तब है जब मैं व्यक्तिगत रूप से उपयोग कर रहा हूं @propertyऔर @attribute.setter(अन्यथा सार्वजनिक विशेषताओं के लिए कोई स्पष्ट एसेस्टर / म्यूटेटर नहीं)।

अंत में, PEP8 और Google स्टाइल गाइड सुझाव:

PEP8, डिजाइनिंग इनहेरिटेंस के लिए कहते हैं:

सरल सार्वजनिक डेटा विशेषताओं के लिए, जटिल अभिगमक / उत्परिवर्ती विधियों के बिना, केवल विशेषता नाम को उजागर करना सबसे अच्छा है । ध्यान रखें कि पायथन भविष्य में वृद्धि के लिए एक आसान मार्ग प्रदान करता है, तो क्या आपको पता होना चाहिए कि एक साधारण डेटा विशेषता को कार्यात्मक व्यवहार को विकसित करने की आवश्यकता है। उस स्थिति में, सरल डेटा विशेषता एक्सेस सिंटैक्स के पीछे कार्यात्मक कार्यान्वयन को छिपाने के लिए गुणों का उपयोग करें।

दूसरी ओर, Google स्टाइल गाइड पायथन लैंग्वेज रूल्स / प्रॉपर्टीज़ के अनुसार अनुशंसा निम्न है:

डेटा का उपयोग करने या सेट करने के लिए नए कोड में गुणों का उपयोग करें जहां आप सामान्य रूप से सरल, हल्के एक्सेसर या सेटर विधियों का उपयोग करते थे। गुण @propertyडेकोरेटर के साथ बनाया जाना चाहिए ।

इस दृष्टिकोण के पेशेवरों:

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

और विपक्ष:

objectपाइथन से विरासत में प्राप्त होना चाहिए 2. ऑपरेटर ओवरलोडिंग की तरह बहुत अधिक दुष्प्रभाव छिपा सकता है। उपवर्गों के लिए भ्रामक हो सकता है।


1
मैं दृढ़ता से असहमत हूँ। यदि मेरी वस्तु पर 15 गुण हैं, और मैं चाहता हूं कि एक की गणना की जाए @property, तो बाकी का उपयोग करना भी @propertyएक खराब निर्णय की तरह लगता है।
क्वालालफ

सहमत हैं लेकिन केवल अगर इस विशेष विशेषता के बारे में कुछ विशिष्ट है जिसकी आपको आवश्यकता @propertyहै (जैसे किसी विशेषता को वापस करने से पहले कुछ विशेष तर्क निष्पादित करना)। अन्यथा आप एक विशेषता को @properyदूसरों के साथ क्यों नहीं सजाएंगे?
टॉमस बर्टकोविएक

@Quelklef पोस्ट में सिडेनोट (तारांकन चिह्न के साथ चिह्नित) देखें।
टॉमस बर्टकोविएक

ठीक है ... यदि आप सिडेनोट द्वारा बताई गई चीजों में से एक नहीं कर रहे हैं, तो आपको @propertyशुरू करने के लिए उपयोग नहीं करना चाहिए , है ना? यदि आपका गेट्टर है return this._xऔर आपका सेटर है this._x = new_x, तो @propertyसभी का उपयोग करना मूर्खतापूर्ण है।
क्लेक्लेफ़

1
हम्म, शायद। मैं व्यक्तिगत रूप से कहूंगा कि यह ठीक नहीं है --- यह पूरी तरह से अति सुंदर है। लेकिन मैं देख सकता हूं कि आप कहां से आ रहे हैं। मुझे लगता है कि मैं आपकी पोस्ट को यह कहते हुए पढ़ता हूं कि "जब उपयोग किया @propertyजा रहा है तो सबसे महत्वपूर्ण चीज है।"
Quelklef

-1

आप जादू विधियों का उपयोग कर सकते हैं __getattribute__और __setattr__

class MyClass:
    def __init__(self, attrvalue):
        self.myattr = attrvalue
    def __getattribute__(self, attr):
        if attr == "myattr":
            #Getter for myattr
    def __setattr__(self, attr):
        if attr == "myattr":
            #Setter for myattr

ज्ञात हो कि __getattr__और __getattribute__समान नहीं हैं। __getattr__केवल तब ही आह्वान किया जाता है जब विशेषता नहीं मिलती है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.