2012-03-29 10 views
24

मेरे पास कई अलग-अलग छोटे वर्ग हैं जिनमें प्रत्येक फ़ील्ड हैं, उदाहरण के लिए इस:मुझे पायथन कक्षाओं से केवल पढ़ने वाले फ़ील्ड का खुलासा कैसे करना चाहिए?

class Article: 
    def __init__(self, name, available): 
     self.name = name 
     self.available = available 

क्या सबसे आसान और/या सबसे मुहावरेदार रास्ता name क्षेत्र केवल पढ़ने के लिए बनाने के लिए है, ताकि

a = Article("Pineapple", True) 
a.name = "Banana" # <-- should not be possible 

अब संभव नहीं है क्या है?

यहाँ मैं अब तक क्या माना जाता है:

  1. एक गेटर का उपयोग करें (! ओह)।

    class Article: 
        def __init__(self, name, available): 
         self._name = name 
         self.available = available 
    
        def name(self): 
         return self._name 
    

    बदसूरत, गैर pythonic - और बॉयलरप्लेट कोड का एक बहुत कुछ लिखने के लिए (विशेष रूप से अगर मैं एक से अधिक फ़ील्ड है बनाने के लिए केवल पढ़ने के लिए)। हालांकि, यह नौकरी करता है और यह देखना आसान है कि ऐसा क्यों है।

  2. उपयोग __setattr__:

    class Article: 
        def __init__(self, name, available): 
         self.name = name 
         self.available = available 
    
        def __setattr__(self, name, value): 
         if name == "name": 
          raise Exception("%s property is read-only" % name) 
         self.__dict__[name] = value 
    

    फोन करने वाले पक्ष पर सुंदर लग रहा है, काम करने के लिए मुहावरेदार तरह से हो रहा है - लेकिन दुर्भाग्य से मैं केवल एक को पढ़ने के बनाने के लिए केवल कुछ क्षेत्रों के साथ कई वर्गों की है। तो मुझे उन सभी को __setattr__ कार्यान्वयन जोड़ने की आवश्यकता होगी। या शायद कुछ प्रकार के मिश्रण का उपयोग करें? किसी भी मामले में, मुझे अपने दिमाग को बनाने की आवश्यकता होगी कि क्लाइंट केवल पढ़ने-योग्य फ़ील्ड को मान निर्दिष्ट करने का प्रयास करता है। कुछ अपवाद पैदा करें, मुझे लगता है - लेकिन कौन सा?

  3. गुणों (और वैकल्पिक रूप से गेटर्स) को स्वचालित रूप से परिभाषित करने के लिए उपयोगिता फ़ंक्शन का उपयोग करें। यह मूलतः एक ही विचार के रूप में (1) सिवाय इसके कि मैं स्पष्ट रूप ही टिककर खेल नहीं लिखते बल्कि करते हैं

    class Article: 
        def __init__(self, name, available): 
         # This function would somehow give a '_name' field to self 
         # and a 'name()' getter to the 'Article' class object (if 
         # necessary); the getter simply returns self._name 
         defineField(self, "name") 
         self.available = available 
    

    कुछ है इस बात का नकारात्मक पक्ष यह है कि मैं भी या अगर यह संभव हो सकता है पता नहीं है (है इसे कैसे कार्यान्वित करें) क्योंकि मैं पायथन में रनटाइम कोड पीढ़ी से परिचित नहीं हूं। :-)

अब तक, (2) तथ्य यह है कि मैं अपने सभी वर्गों के लिए __setattr__ परिभाषाओं की आवश्यकता होगी के अलावा मेरे लिए सबसे होनहार प्रतीत होता है। मेरी इच्छा है कि 'एनोटेट' फ़ील्ड का एक तरीका था ताकि यह स्वचालित रूप से हो। क्या किसी के पास एक बेहतर विचार है?

इसके लायक होने के लिए, मैं पायथन 2.6 गाता हूं।

अद्यतन: सभी दिलचस्प प्रतिक्रियाओं के लिए धन्यवाद! अब तक, मेरे पास यह है:

def ro_property(o, name, value): 
    setattr(o.__class__, name, property(lambda o: o.__dict__["_" + name])) 
    setattr(o, "_" + name, value) 

class Article(object): 
    def __init__(self, name, available): 
     ro_property(self, "name", name) 
     self.available = available 

ऐसा लगता है कि यह काफी अच्छी तरह से काम करता है। केवल परिवर्तन मूल वर्ग के लिए आवश्यक हैं

  • मैं object वारिस की जरूरत है (जो इस तरह के एक बेवकूफ बात वैसे भी नहीं है, मुझे लगता है)
  • मैं ro_property(self, "name", name) को self._name = name बदलने की जरूरत है।

यह मेरे लिए काफी साफ दिखता है - क्या कोई इसके साथ नकारात्मक देख सकता है?

+2

आपकी 'ro_property' बहुत जटिल है - या तो स्वेन या क्रिस का जवाब जाने का तरीका है। –

+0

@EthanFurman यह मुश्किल है, लेकिन शायद वह कीमत है जो आपको भुगतान करना है यदि आप मौजूदा कक्षाओं में किए गए परिवर्तनों को कम से कम रखना चाहते हैं? –

+0

मैं वास्तव में याद नहीं कर सकता जब मैंने आखिरकार एक इंस्टेंस की विशेषता (उदाहरण के तरीकों के बाहर से) लिखा था, सिवाय इसके कि बंदर-किसी और के कोड को पैच करते समय। उदाहरण के उदाहरण नहीं हैं लगभग हमेशा अभ्यास में पढ़ने के लिए? इंस्टेंस गुण आमतौर पर '__init__' में सेट होते हैं और बाद में नहीं बदला जाता है, जब तक कि इंस्टेंस पर कोई विधि न कहें। मुझे लगता है कि मैं इस उचित कोडिंग शैली पर विचार करता हूं। –

उत्तर

37

मैं एक डेकोरेटर के रूप में property का प्रयोग करेंगे (name के लिए अपने गेटर प्रबंधन करने के लिए दस्तावेज में वर्ग Parrot के लिए उदाहरण देखें)। का प्रयोग करें, उदाहरण के लिए, कुछ की तरह:

class Article(object): 
    def __init__(self, name, available): 
     self._name = name 
     self.available = available 

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

आप name संपत्ति (डेकोरेटर x.setter एक समारोह के आसपास का प्रयोग करके) के लिए सेटर को परिभाषित नहीं है, तो इस फेंकता एक AttributeError जब आप कोशिश करते हैं और रीसेट name

नोट: आप अजगर की नई शैली वर्गों का उपयोग करने के (अर्थात अजगर 2.6 में आप object से विरासत के लिए है) गुण ठीक से कार्य करने के लिए। यह @SvenMarnach के अनुसार मामला नहीं है।

+2

यह कॉलर पक्ष पर सुंदर दिखता है, लेकिन वास्तव में कक्षाओं को लिखते समय यह बॉयलर प्लेट की तरह दिखता है। यह सुंदर होगा अगर मैं कुछ @ @property self_name = name' या तो कन्स्ट्रक्टर में ऐसा कर सकता था ताकि पायथन मेरे लिए गेटर को स्वचालित रूप से परिभाषित कर सके। जैसा कि है, यह कोड कॉलर के लिए अच्छा है, लेकिन सादे गेटर ('@ property' के लिए अतिरिक्त पंक्ति) की तुलना में कक्षा लेखक के लिए लिखने के लिए और भी कोड। –

+0

कुछ '@property self._name = name' जैसा कुछ अच्छा होगा, लेकिन सजावटी वस्तुओं की बजाय कार्यों पर काम करते हैं। यह आपके गेटटर विचार पर एक अतिरिक्त रेखा है, लेकिन यह करने का यह बेवकूफ तरीका है, और जिस तरह से पाइथन दस्तावेज केवल पढ़ने योग्य वर्ग चर बनाने का प्रदर्शन करता है। आखिरकार कक्षा चर के डिफ़ॉल्ट व्यवहार को फिर से परिभाषित करने के लिए आपको हमेशा कुछ बॉयलरप्लेट कोड की आवश्यकता होगी। – Chris

+1

[गुण पुराने शैली के वर्गों पर भी काम करते हैं।] (Http://ideone.com/wgXAX) वर्णनकर्ता स्वयं, यानी वर्ग 'संपत्ति' स्वयं, एक नई शैली की कक्षा होना चाहिए, लेकिन यह है। पाइथन दस्तावेज इस संबंध में थोड़ा भ्रामक है। –

2

मैं अपने विकल्प 1 के साथ चिपके रहते हैं, लेकिन यह अजगर संपत्ति का उपयोग करने के लिए परिष्कृत होगा:

class Article 
    def get_name(self): 
     return self.__name 

    name = property(get_name) 
+4

डबल अंडरस्कोर सबक्लास को गलती से खेतों में ओवरराइड करने से रोकने के लिए हैं। नियमित "निजी" फ़ील्ड के लिए एकल अंडरस्कोर सही है। साथ ही, सजावट के रूप में 'संपत्ति' का उपयोग करना बेहतर है। – agf

+1

एचएम, दिलचस्पी लगता है (विशेष रूप से अगर मैं 'संपत्ति' में लैम्ब्डा पास कर सकता हूं ...), लेकिन मुझे लगता है कि यह काम नहीं कर रहा है। 'get_name' एक वैश्विक विधि होगी, और 'नाम' असाइनमेंट कन्स्ट्रक्टर में किया जाएगा? –

+1

धन्यवाद एएफएफ। उपरोक्त कोड को आपकी कक्षा में रखा जाना चाहिए, न कि वैश्विक स्तर पर। – Pinch

4

यह ध्यान दिया जाना चाहिए कि यह हमेशा पाइथन में किसी ऑब्जेक्ट के गुणों को संशोधित करने के लिए संभव है - Python में कोई वास्तव में निजी चर नहीं हैं। यह सिर्फ कुछ दृष्टिकोण इसे थोड़ा कठिन बनाते हैं। लेकिन एक निर्धारित कोडर हमेशा एक विशेषता के मूल्य को देख और संशोधित कर सकता है। उदाहरण के लिए, यदि मैं चाहता हूं कि मैं हमेशा __setattr__ संशोधित कर सकता हूं ...

अधिक जानकारी के लिए, पाइथन ट्यूटोरियल के Section 9.6 देखें। पाइथन नाम mangling का उपयोग करता है जब गुण __ के साथ उपसर्ग किए जाते हैं, इसलिए रनटाइम पर वास्तविक नाम अलग होता है लेकिन आप अभी भी रनटाइम पर वह नाम प्राप्त कर सकते हैं (और इस प्रकार विशेषता को संशोधित करते हैं)।

8

क्रिस जवाब में आधार पर, लेकिन यकीनन अधिक pythonic:

def ro_property(field): 
    return property(lambda self : self.__dict__[field]) 

class Article(object): 
    name = ro_property('_name') 

    def __init__(self): 
     self._name = "banana" 

संपत्ति यह एक AttributeError बढ़ा देंगे संशोधित करने के लिए कोशिश कर रहा है।

a = Article() 
print a.name # -> 'banana' 
a.name = 'apple' # -> AttributeError: can't set attribute 

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

के बारे में क्या ?:

def ro_property(name): 
    def ro_property_decorator(c): 
     setattr(c, name, property(lambda o: o.__dict__["_" + name])) 
     return c 
    return ro_property_decorator 

@ro_property('name') 
@ro_property('other') 
class Article(object): 
    def __init__(self, name): 
     self._name = name 
     self._other = "foo" 

a = Article("banana") 
print a.name # -> 'banana' 
a.name = 'apple' # -> AttributeError: can't set attribute 

कक्षा सज्जाकार कल्पना कर रहे हैं __init__ समारोह के बाहर ro_property कॉल डाल है!

+0

मुझे यह पसंद है कि इससे कक्षाओं में किए जाने वाले बदलावों की मात्रा कम हो जाती है। –

+0

इस उत्तर के लिए धन्यवाद एक टन, कृपया मेरे पास अब मेरे अपडेट किए गए उत्तर को देखें। यह मुझे केवल वही चीज़ प्राप्त करता है जो मुझे लगता है, और मूल वर्ग के लिए चगने सिर्फ एक पंक्ति है ('self._prop = prop' के बजाय अब मैं' ro_property (self, "prop", prop) ') करता हूं। –

+0

कृपया, मेरे नए अपडेट के साथ मेरा अद्यतन उत्तर देखें। – rodrigo

12

जैसा कि अन्य उत्तरों में बताया गया है, संपत्ति का उपयोग करना केवल-पढ़ने योग्य विशेषताओं के लिए जाने का तरीका है। Chris' answer में समाधान सबसे साफ है: यह property() का उपयोग सीधे-सीधे, सरल तरीके से अंतर्निहित है। पाइथन से परिचित हर कोई इस पैटर्न को पहचान लेगा, और कोई डोमेन-विशिष्ट वूडू नहीं हो रहा है।

from operator import attrgetter 

class Article(object): 
    def __init__(self, name, available): 
     self._name = name 
     self.available = available 
    name = property(attrgetter("_name")) 

आम तौर पर, मैं डोमेन-विशिष्ट कार्यों को परिभाषित कुछ है कि कर सकते हैं करने के लिए पसंद नहीं है:

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

+2

* के बारे में आपका तर्क * क्यों * क्रिस का जवाब इतना अच्छा है कि वास्तव में मुझे जीत लिया। मैंने सोचा कि रॉड्रिगो द्वारा पोस्ट की गई चीज़ों के साथ कुछ चतुर चाल वास्तव में साफ होगी लेकिन वास्तव में, मुझे एक जगह में इतने सारे जादू के लिए कुछ उठाए भौहें मिल गईं। –

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

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