वर्ग और उदाहरण विशेषताओं के बीच अंतर क्या है?


132

क्या कोई सार्थक अंतर है:

class A(object):
    foo = 5   # some default value

बनाम

class B(object):
    def __init__(self, foo=5):
        self.foo = foo

यदि आप बहुत सारे उदाहरण बना रहे हैं, तो क्या दो शैलियों के प्रदर्शन या अंतरिक्ष आवश्यकताओं में कोई अंतर है? जब आप कोड पढ़ते हैं, तो क्या आप दो शैलियों के अर्थ को काफी भिन्न मानते हैं?


1
मुझे बस एहसास हुआ कि इसी तरह का प्रश्न पूछा गया था और यहां उत्तर दिया गया: stackoverflow.com/questions/206734/… क्या मुझे इस प्रश्न को हटा देना चाहिए?
डैन होमरिक

2
यह आपका प्रश्न है, इसे हटाने के लिए स्वतंत्र महसूस करें। चूंकि यह आपका है, इसलिए किसी और की राय क्यों पूछें?
एस.लूट।

जवाबों:


146

प्रदर्शन से परे, एक महत्वपूर्ण शब्दार्थ अंतर है। वर्ग विशेषता मामले में, केवल एक वस्तु को संदर्भित किया जाता है। उदाहरण-विशेषता-सेट-एट-इंस्टेंटेशन में, संदर्भित कई ऑब्जेक्ट हो सकते हैं। उदाहरण के लिए

>>> class A: foo = []
>>> a, b = A(), A()
>>> a.foo.append(5)
>>> b.foo
[5]
>>> class A:
...  def __init__(self): self.foo = []
>>> a, b = A(), A()
>>> a.foo.append(5)
>>> b.foo    
[]

4
केवल उत्परिवर्तनीय प्रकार साझा किए जाते हैं। की तरह intऔर strवे अभी भी कक्षा के बजाय प्रत्येक उदाहरण के साथ संलग्न हैं।
बाबू

11
@Babu: नहीं, intऔर strकर रहे हैं भी बिल्कुल उसी तरह साझा,। आप आसानी से isया के साथ जाँच कर सकते हैं id। या सिर्फ प्रत्येक उदाहरण में __dict__और वर्ग के देखें __dict__। यह आमतौर पर बहुत ज्यादा मायने नहीं रखता है कि अपरिवर्तनीय प्रकार साझा किए जाते हैं या नहीं।
22

17
हालांकि, ध्यान दें कि यदि आप करते हैं a.foo = 5, तो दोनों मामलों में आपको b.fooवापसी दिखाई देगी []। ऐसा इसलिए है क्योंकि पहले मामले में, आप a.fooएक ही नाम के नए इंस्टेंस विशेषता के साथ वर्ग विशेषता को ओवरराइट कर रहे हैं ।
कॉन्स्टेंटिन शुबर्ट

39

अंतर यह है कि वर्ग पर विशेषता सभी उदाहरणों द्वारा साझा की जाती है। एक उदाहरण पर विशेषता उस उदाहरण के लिए अद्वितीय है।

यदि C ++ से आते हैं, तो क्लास पर विशेषताएँ स्थिर सदस्य चर की तरह होती हैं।


1
क्या यह केवल साझा किए जाने योग्य प्रकार नहीं हैं जो साझा किए जाते हैं? स्वीकृत उत्तर एक सूची दिखाता है, जो काम करता है, लेकिन अगर यह एक इंट है, तो यह एक उदाहरण के रूप में ही प्रतीत होता है attr: >>> class A(object): foo = 5 >>> a, b = A(), A() >>> a.foo = 10 >>> b.foo 5
Rafe

6
@ कैफे: नहीं, सभी प्रकार साझा किए जाते हैं। आपके भ्रमित होने का कारण यह है कि a.foo.append(5)उस मूल्य को क्या बदल रहा है, जो मूल्य के a.fooलिए एक नया नाम a.foo = 5बना a.fooरहा है 5। तो, आप एक उदाहरण विशेषता के साथ समाप्त होते हैं जो वर्ग विशेषता को छुपाता है। a.foo = 5एलेक्स के संस्करण में भी यही कोशिश करें और आप देखेंगे कि b.fooअपरिवर्तित है।
अवतरण

30

यहाँ एक बहुत अच्छी पोस्ट है , और इसे नीचे के रूप में सारांशित करें।

class Bar(object):
    ## No need for dot syntax
    class_var = 1

    def __init__(self, i_var):
        self.i_var = i_var

## Need dot syntax as we've left scope of class namespace
Bar.class_var
## 1
foo = MyClass(2)

## Finds i_var in foo's instance namespace
foo.i_var
## 2

## Doesn't find class_var in instance namespace…
## So look's in class namespace (Bar.__dict__)
foo.class_var
## 1

और दृश्य रूप में

यहाँ छवि विवरण दर्ज करें

कक्षा विशेषता असाइनमेंट

  • यदि एक वर्ग विशेषता को वर्ग तक पहुँच कर सेट किया जाता है, तो यह सभी उदाहरणों के लिए मान को ओवरराइड करेगा

    foo = Bar(2)
    foo.class_var
    ## 1
    Bar.class_var = 2
    foo.class_var
    ## 2
    
  • यदि कोई वर्ग चर को किसी इंस्टेंस पर पहुँच कर सेट किया जाता है, तो यह केवल उस उदाहरण के लिए मान को ओवरराइड करेगा । यह अनिवार्य रूप से वर्ग चर को ओवरराइड करता है और इसे सहज रूप से उपलब्ध उदाहरण चर में बदल देता है, केवल उस उदाहरण के लिए

    foo = Bar(2)
    foo.class_var
    ## 1
    foo.class_var = 2
    foo.class_var
    ## 2
    Bar.class_var
    ## 1
    

आप वर्ग विशेषता का उपयोग कब करेंगे?

  • स्थिरांक स्थिर करना । जैसे ही क्लास की विशेषताओं को क्लास की विशेषताओं के रूप में एक्सेस किया जा सकता है, क्लास-वाइड, क्लास-विशिष्ट स्थिरांक के भंडारण के लिए उनका उपयोग करना अक्सर अच्छा होता है

    class Circle(object):
         pi = 3.14159
    
         def __init__(self, radius):
              self.radius = radius   
        def area(self):
             return Circle.pi * self.radius * self.radius
    
    Circle.pi
    ## 3.14159
    c = Circle(10)
    c.pi
    ## 3.14159
    c.area()
    ## 314.159
    
  • डिफ़ॉल्ट मानों को परिभाषित करना । एक तुच्छ उदाहरण के रूप में, हम एक बंधी हुई सूची (यानी, एक सूची जो केवल कुछ तत्वों की संख्या को कम या कम कर सकते हैं) बना सकते हैं और 10 वस्तुओं की एक डिफ़ॉल्ट टोपी का चयन कर सकते हैं

    class MyClass(object):
        limit = 10
    
        def __init__(self):
            self.data = []
        def item(self, i):
            return self.data[i]
    
        def add(self, e):
            if len(self.data) >= self.limit:
                raise Exception("Too many elements")
            self.data.append(e)
    
     MyClass.limit
     ## 10
    

19

चूंकि लोग यहां टिप्पणियों में और दो अन्य प्रश्नों के रूप में चिह्नित किए गए हैं, जो सभी के लिए एक ही तरीके से इस बारे में भ्रमित दिखाई देते हैं, मुझे लगता है कि यह एलेक्स कॉवेंट्री के शीर्ष पर एक अतिरिक्त जवाब जोड़ने के लायक है ।

तथ्य यह है कि एलेक्स एक सूची की तरह, एक उत्परिवर्तित प्रकार का मूल्य प्रदान कर रहा है, इसका कोई लेना-देना नहीं है कि चीजें साझा की जाती हैं या नहीं। हम इसे idफंक्शन या isऑपरेटर के साथ देख सकते हैं :

>>> class A: foo = object()
>>> a, b = A(), A()
>>> a.foo is b.foo
True
>>> class A:
...     def __init__(self): self.foo = object()
>>> a, b = A(), A()
>>> a.foo is b.foo
False

(यदि आप सोच रहे हैं कि मैंने object()इसके बजाय इसका उपयोग क्यों किया , तो कहना चाहिए 5कि दो अन्य मुद्दों पर चलने से बचना चाहिए, जिन्हें मैं यहाँ नहीं करना चाहता; दो अलग-अलग कारणों से, पूरी तरह से अलग-अलग बनाये 5जा सकते हैं। संख्या का एक ही उदाहरण 5। लेकिन पूरी तरह से अलग-अलग बनाया object()नहीं जा सकता।)


तो, ऐसा क्यों है कि a.foo.append(5)एलेक्स का उदाहरण प्रभावित करता है b.foo, लेकिन a.foo = 5मेरे उदाहरण में नहीं है? खैर, a.foo = 5एलेक्स के उदाहरण में प्रयास करें , और ध्यान दें कि यह b.fooवहाँ भी प्रभावित नहीं करता है ।

a.foo = 5सिर्फ a.fooनाम के लिए बना रहा है 5। यह प्रभावित नहीं करता है b.foo, या पुराने मूल्य के लिए किसी भी अन्य नाम का a.fooउपयोग करता है। * यह थोड़ा मुश्किल है कि हम एक उदाहरण विशेषता बना रहे हैं जो एक वर्ग विशेषता को छुपाता है, ** लेकिन एक बार जब आप इसे प्राप्त करते हैं, तो कुछ भी जटिल नहीं है यहाँ हो रहा है।


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


* C ++ जैसी भाषा से आने वाले लोगों के लिए भ्रम यह है कि पायथन में, मूल्यों को चर में संग्रहीत नहीं किया जाता है। मूल्य मूल्य-भूमि में रहते हैं, अपने दम पर, चर केवल मूल्यों के लिए नाम हैं, और असाइनमेंट सिर्फ एक नया नाम मान के लिए बनाता है। यदि यह मदद करता है, तो एक के shared_ptr<T>बजाय प्रत्येक पायथन चर के बारे में सोचें T

** कुछ लोग एक उदाहरण विशेषता के लिए "डिफ़ॉल्ट मान" के रूप में एक वर्ग विशेषता का उपयोग करके इसका लाभ उठाते हैं जो उदाहरण सेट हो सकते हैं या नहीं हो सकते हैं। यह कुछ मामलों में उपयोगी हो सकता है, लेकिन यह भ्रामक भी हो सकता है, इसलिए इसके साथ सावधान रहें।


0

एक और स्थिति है।

वर्ग और उदाहरण विशेषताएँ वर्णनात्मक है

# -*- encoding: utf-8 -*-


class RevealAccess(object):
    def __init__(self, initval=None, name='var'):
        self.val = initval
        self.name = name

    def __get__(self, obj, objtype):
        return self.val


class Base(object):
    attr_1 = RevealAccess(10, 'var "x"')

    def __init__(self):
        self.attr_2 = RevealAccess(10, 'var "x"')


def main():
    b = Base()
    print("Access to class attribute, return: ", Base.attr_1)
    print("Access to instance attribute, return: ", b.attr_2)

if __name__ == '__main__':
    main()

ऊपर उत्पादन होगा:

('Access to class attribute, return: ', 10)
('Access to instance attribute, return: ', <__main__.RevealAccess object at 0x10184eb50>)

वर्ग या उदाहरण के माध्यम से एक ही प्रकार की आवृत्ति का उपयोग अलग परिणाम देता है!

और मुझे c.yObject_GenericGetAttr परिभाषा And और एक बेहतरीन पोस्ट मिली ।

समझाना

यदि गुण वर्गों के शब्दकोश में पाया जाता है जो बनाते हैं। ऑब्जेक्ट MRO, फिर यह देखने के लिए जांचें कि क्या विशेषता किसी डेटा डिस्क्रिप्टर (जो कुछ और नहीं है कि एक वर्ग दोनों __get__और __set__विधियों को लागू करने के लिए अधिक है ) को इंगित करता है । यदि ऐसा होता है, __get__तो डेटा डिस्क्रिप्टिव (लाइनें 28-33) की विधि को कॉल करके विशेषता लुकअप को हल करें ।

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