उदाहरणों के बीच वर्ग डेटा साझा करने से कैसे बचें?


145

मुझे यह व्यवहार चाहिए

class a:
    list = []

x = a()
y = a()

x.list.append(1)
y.list.append(2)
x.list.append(3)
y.list.append(4)

print(x.list) # prints [1, 3]
print(y.list) # prints [2, 4]

बेशक, जब मैं प्रिंट करता हूं तो वास्तव में क्या होता है:

print(x.list) # prints [1, 2, 3, 4]
print(y.list) # prints [1, 2, 3, 4]

स्पष्ट रूप से वे डेटा को कक्षा में साझा कर रहे हैं a। मैं अपनी इच्छा के व्यवहार को प्राप्त करने के लिए अलग-अलग उदाहरण कैसे प्राप्त करूं?


14
कृपया, listविशेषता नाम के रूप में उपयोग न करें । listएक नई सूची बनाने के लिए एक अंतर्निहित कार्य है। आपको नाम अक्षर के साथ बड़े अक्षर लिखने चाहिए।
मार्क टुड़ी

जवाबों:


147

आप यह चाहते हैं:

class a:
    def __init__(self):
        self.list = []

वर्ग घोषणा के अंदर चरों को घोषित करने से वे "वर्ग" सदस्य बन जाते हैं न कि सदस्य। उन्हें __init__विधि के अंदर घोषित करने से यह सुनिश्चित हो जाता है कि सदस्यों के एक नए उदाहरण को ऑब्जेक्ट के हर नए उदाहरण के साथ बनाया गया है, जो आपके द्वारा खोजा जा रहा व्यवहार है।


5
एक अतिरिक्त स्पष्टीकरण: यदि आप उदाहरणों में से किसी एक में सूची संपत्ति को फिर से असाइन करना चाहते हैं, तो यह दूसरों को प्रभावित नहीं करेगा। इसलिए यदि आपने ऐसा कुछ किया है x.list = [], तो आप इसे बदल सकते हैं और किसी अन्य को प्रभावित नहीं कर सकते। आपके सामने जो समस्या है , वह वही है x.listऔर y.listवही सूची है, इसलिए जब आप एक पर एपेंड कहते हैं, तो यह दूसरे को प्रभावित करता है।
मैट मोरियारिटी 14

लेकिन ऐसा केवल सूची के लिए ही क्यों होता है? जब मैंने init के बाहर एक पूर्णांक या स्ट्रिंग घोषित किया , तो यह वस्तुओं के बीच साझा नहीं किया गया था? क्या कोई भी इस अवधारणा से कोई डॉक लिंक साझा कर सकता है?
अमल त्स

1
@ एएमएलटी ऐसा लगता है कि आप समझ नहीं पा रहे हैं कि अजगर कैसे काम करता है। देखें इस वीडियो या इस अतः पद । आपके द्वारा देखा जाने वाला व्यवहार इस तथ्य के कारण होता है कि आप सूचियों को म्यूट कर रहे हैं, लेकिन चींटियों और तारों के संदर्भ में विद्रोह कर रहे हैं।
Андрей Беньковский

4
@ एएमएलटीएस नोट: यह क्लास विशेषताओं का उपयोग करने के लिए एक बुरा अभ्यास माना जाता है उदाहरण के लिए "आलसी" डिफ़ॉल्ट मान। भले ही विशेषताएँ एक अपरिवर्तनीय प्रकार की हों, लेकिन उन्हें अंदर असाइन करना बेहतर होता है __init__
Андрей Беньковский

21

स्वीकृत उत्तर काम करता है लेकिन थोड़ा अधिक स्पष्टीकरण चोट नहीं पहुंचाता है।

जब उदाहरण बनाया जाता है तो वर्ग विशेषताएँ उदाहरण विशेषताएँ नहीं बन जाती हैं। वे उदाहरण विशेषताएँ बन जाते हैं जब एक मूल्य उन्हें सौंपा जाता है।

मूल कोड में listतात्कालिकता के बाद विशेषता के लिए कोई मूल्य नहीं सौंपा गया है; इसलिए यह एक वर्गीय विशेषता बनी हुई है। __init__काम के अंदर सूची को परिभाषित करना क्योंकि __init__तात्कालिकता के बाद कहा जाता है। वैकल्पिक रूप से, यह कोड वांछित आउटपुट भी उत्पन्न करेगा:

>>> class a:
    list = []

>>> y = a()
>>> x = a()
>>> x.list = []
>>> y.list = []
>>> x.list.append(1)
>>> y.list.append(2)
>>> x.list.append(3)
>>> y.list.append(4)
>>> print(x.list)
[1, 3]
>>> print(y.list)
[2, 4]

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

>>> class a:
    string = ''


>>> x = a()
>>> y = a()
>>> x.string += 'x'
>>> y.string += 'y'
>>> x.string
'x'
>>> y.string
'y'

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


11

आपने "सूची" को "श्रेणी स्तर की संपत्ति" के रूप में घोषित किया, न कि "उदाहरण स्तर की संपत्ति" के रूप में। उदाहरण के स्तर पर स्कॉप किए गए गुणों के लिए, आपको __init__विधि में "स्वयं" पैरामीटर (या स्थिति के आधार पर कहीं और) के साथ संदर्भित करने के माध्यम से उन्हें आरंभीकृत करने की आवश्यकता है ।

आपको कड़ाई से उदाहरण के गुणों को __init__विधि में इनिशियलाइज़ करना नहीं है, लेकिन यह आसान समझ के लिए बनाता है।


11

यद्यपि स्वीकृत एवर ऑन है, मैं थोड़ा वर्णन जोड़ना चाहूंगा।

चलो एक छोटा सा व्यायाम करते हैं

सबसे पहले एक वर्ग को इस प्रकार परिभाषित करें:

class A:
    temp = 'Skyharbor'

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

    def change(self, y):
        self.temp = y

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

  • हमारे पास एक बहुत ही सरल वर्ग है जिसमें एक विशेषता है tempजो एक स्ट्रिंग है
  • एक __init__विधि जो सेट करती हैself.x
  • एक परिवर्तन विधि सेट करती है self.temp

बहुत सीधे आगे अब तक हाँ? अब हम इस वर्ग के साथ खेलना शुरू करते हैं। आइए पहले इस वर्ग को शुरू करें:

a = A('Tesseract')

अब निम्नलिखित करें:

>>> print(a.temp)
Skyharbor
>>> print(A.temp)
Skyharbor

ठीक है, a.tempउम्मीद के मुताबिक A.tempकाम किया लेकिन नरक कैसे हुआ ? खैर यह काम किया क्योंकि अस्थायी एक वर्ग विशेषता है। अजगर में सब कुछ एक वस्तु है। यहाँ A भी वर्ग की एक वस्तु है type। इस प्रकार विशेषता अस्थायी Aवर्ग द्वारा आयोजित एक विशेषता है और यदि आप A(और नहीं के माध्यम से a) अस्थायी के मूल्य को बदलते हैं , तो परिवर्तित मूल्य Aवर्ग के सभी उदाहरणों में परिलक्षित होने वाला है । आइये आगे बढ़ते हैं और करते हैं:

>>> A.temp = 'Monuments'
>>> print(A.temp)
Monuments
>>> print(a.temp)
Monuments

दिलचस्प है ना? और ध्यान दें कि id(a.temp)और id(A.temp)अब भी वही कर रहे हैं

किसी भी पायथन ऑब्जेक्ट को स्वचालित रूप से एक __dict__विशेषता दी जाती है , जिसमें उसकी विशेषताओं की सूची होती है। आइए देखें कि इस उदाहरण में हमारे उदाहरण वस्तुओं के लिए क्या है:

>>> print(A.__dict__)
{
    'change': <function change at 0x7f5e26fee6e0>,
    '__module__': '__main__',
    '__init__': <function __init__ at 0x7f5e26fee668>,
    'temp': 'Monuments',
    '__doc__': None
}
>>> print(a.__dict__)
{x: 'Tesseract'}

ध्यान दें कि tempविशेषता Aवर्ग की विशेषताओं xमें सूचीबद्ध है जबकि उदाहरण के लिए सूचीबद्ध है।

तो कैसे आए कि हम एक परिभाषित मूल्य प्राप्त करें a.tempअगर यह भी उदाहरण के लिए सूचीबद्ध नहीं है a। खैर यह __getattribute__()विधि का जादू है । पायथन में बिंदीदार वाक्यविन्यास स्वचालित रूप से इस पद्धति को लागू करता है इसलिए जब हम लिखते हैं a.temp, तो पायथन निष्पादित होता है a.__getattribute__('temp')। वह विधि विशेषता लुकअप क्रिया करती है, अर्थात अलग-अलग स्थानों में देख कर विशेषता का मान प्राप्त करती है।

किसी वस्तु के __getattribute__()पहले आंतरिक शब्दकोश ( तानाशाही ) की खोज का मानक कार्यान्वयन , फिर वस्तु का प्रकार। इस मामले में a.__getattribute__('temp')पहले a.__dict__['temp']और फिर निष्पादित करता हैa.__class__.__dict__['temp']

ठीक है अब चलो हमारी changeविधि का उपयोग करें :

>>> a.change('Intervals')
>>> print(a.temp)
Intervals
>>> print(A.temp)
Monuments

खैर अब जो हमने उपयोग किया है self, print(a.temp)वह हमें एक अलग मूल्य देता है print(A.temp)

अब यदि हम तुलना करते हैं id(a.temp)और id(A.temp), वे अलग होंगे।


3

हां, आपको "कंस्ट्रक्टर" में घोषित करना होगा यदि आप चाहते हैं कि सूची एक ऑब्जेक्ट प्रॉपर्टी बन जाए न कि क्लास प्रॉपर्टी।


3

यहाँ लगभग हर प्रतिक्रिया एक विशेष बिंदु को याद करती है। नीचे दिए गए कोड के अनुसार वर्ग चर कभी भी उदाहरण चर नहीं बनते हैं। वर्ग स्तर पर चर असाइनमेंट को इंटरसेप्ट करने के लिए मेटाक्लस का उपयोग करके, हम देख सकते हैं कि जब a.myattr को पुन: असाइन किया जाता है, तो क्लास पर फ़ील्ड असाइनमेंट मैजिक विधि को नहीं बुलाया जाता है। ऐसा इसलिए है क्योंकि असाइनमेंट एक नया इंस्टेंस वेरिएबल बनाता है । इस व्यवहार का वर्ग चर से कोई लेना- देना नहीं है जैसा कि दूसरी कक्षा द्वारा प्रदर्शित किया गया है जिसमें कोई वर्ग चर नहीं है और फिर भी क्षेत्र असाइनमेंट की अनुमति देता है।

class mymeta(type):
    def __init__(cls, name, bases, d):
        pass

    def __setattr__(cls, attr, value):
        print("setting " + attr)
        super(mymeta, cls).__setattr__(attr, value)

class myclass(object):
    __metaclass__ = mymeta
    myattr = []

a = myclass()
a.myattr = []           #NOTHING IS PRINTED
myclass.myattr = [5]    #change is printed here
b = myclass()
print(b.myattr)         #pass through lookup on the base class

class expando(object):
    pass

a = expando()
a.random = 5            #no class variable required
print(a.random)         #but it still works

शॉर्ट क्लास चर में उदाहरण चर के साथ कुछ भी नहीं है।

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


0

अन्य उदाहरणों द्वारा साझा किए गए अपने चर की रक्षा करने के लिए आपको एक उदाहरण बनाते समय हर बार नए उदाहरण चर बनाने की आवश्यकता होती है। जब आप एक वर्ग के अंदर एक चर घोषित कर रहे हैं तो यह कक्षा चर है और सभी उदाहरणों द्वारा साझा किया गया है। यदि आप इसे उदाहरण के लिए बनाना चाहते हैं तो उदाहरण के लिए चर को फिर से संगठित करने के लिए init विधि का उपयोग करना होगा

से अजगर ऑब्जेक्ट्स और कक्षा Programiz.com द्वारा :

__init__()समारोह। जब भी उस वर्ग की कोई नई वस्तु त्वरित की जाती है तो यह विशेष कार्य कहलाता है।

इस प्रकार के फ़ंक्शन को ऑब्जेक्ट ओरिएंटेड प्रोग्रामिंग (OOP) में कंस्ट्रक्टर भी कहा जाता है। हम आम तौर पर सभी चर को इनिशियलाइज़ करने के लिए इसका उपयोग करते हैं।

उदाहरण के लिए:

class example:
    list=[] #This is class variable shared by all instance
    def __init__(self):
        self.list = [] #This is instance variable referred to specific instance
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.