पायथन में उदाहरण चर के लिए मुझे डिफ़ॉल्ट मान कैसे घोषित करना चाहिए?


90

क्या मुझे अपने वर्ग के सदस्यों को इस तरह डिफ़ॉल्ट मान देना चाहिए:

class Foo:
    num = 1

या इस तरह?

class Foo:
    def __init__(self):
        self.num = 1

में इस सवाल का मैं दोनों ही मामलों में पता चला कि,

bar = Foo()
bar.num += 1

एक अच्छी तरह से परिभाषित ऑपरेशन है।

मैं समझता हूं कि पहला तरीका मुझे एक वर्ग चर देगा, जबकि दूसरा नहीं होगा। हालाँकि, अगर मुझे एक वर्ग चर की आवश्यकता नहीं है, लेकिन केवल मेरे उदाहरण चर के लिए एक डिफ़ॉल्ट मान सेट करने की आवश्यकता है, क्या दोनों विधियां समान रूप से अच्छी हैं? या उनमें से एक दूसरे की तुलना में अधिक 'पायथोनिक' है?

एक बात मैंने देखी है कि Django ट्यूटोरियल में, वे मॉडल घोषित करने के लिए दूसरी विधि का उपयोग करते हैं। व्यक्तिगत रूप से मुझे लगता है कि दूसरी विधि अधिक सुरुचिपूर्ण है, लेकिन मैं जानना चाहूंगा कि 'मानक' तरीका क्या है।

जवाबों:


132

Bp के उत्तर का विस्तार करते हुए, मैं आपको दिखाना चाहता था कि अपरिवर्तनीय प्रकारों से उसका क्या मतलब है।

सबसे पहले, यह ठीक है:

>>> class TestB():
...     def __init__(self, attr=1):
...         self.attr = attr
...     
>>> a = TestB()
>>> b = TestB()
>>> a.attr = 2
>>> a.attr
2
>>> b.attr
1

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

>>> class Test():
...     def __init__(self, attr=[]):
...         self.attr = attr
...     
>>> a = Test()
>>> b = Test()
>>> a.attr.append(1)
>>> a.attr
[1]
>>> b.attr
[1]
>>> 

ध्यान दें कि दोनों aऔर bएक साझा विशेषता है। यह अक्सर अवांछित होता है।

यह उदाहरण चर के लिए डिफ़ॉल्ट मानों को परिभाषित करने का पाइथोनिक तरीका है, जब प्रकार परिवर्तनशील होता है:

>>> class TestC():
...     def __init__(self, attr=None):
...         if attr is None:
...             attr = []
...         self.attr = attr
...     
>>> a = TestC()
>>> b = TestC()
>>> a.attr.append(1)
>>> a.attr
[1]
>>> b.attr
[]

कोड कार्यों का मेरा पहला स्निपेट कारण है, क्योंकि अपरिवर्तनीय प्रकारों के साथ, पायथन जब भी चाहे एक नया उदाहरण बनाता है। यदि आपको 1 से 1 जोड़ने की आवश्यकता है, तो पायथन आपके लिए एक नया 2 बनाता है, क्योंकि पुराने 1 को बदला नहीं जा सकता है। इसका कारण ज्यादातर हैशिंग है, मेरा मानना ​​है।


2
मैं हाल ही में परिवर्तनशील विशेषताओं के लिए डिफ़ॉल्ट मानों को लागू करने के इस सरल तरीके से आया हूं। बस विधि के self.attr = attr or []भीतर लिखें __init__। यह उसी परिणाम को प्राप्त करता है (मुझे लगता है) और अभी भी स्पष्ट और पठनीय है।
बिल

4
क्षमा करें दोस्तों। मैंने अपने उपरोक्त टिप्पणी में सुझाव पर कुछ और शोध किया और स्पष्ट रूप से यह बिल्कुल समान नहीं है और इसकी सिफारिश नहीं की गई है
बिल

क्लास टेस्टसी में खाली कोष्ठक क्यों है? क्या वह पायथन 2 विरासत है?
क्रिस जीपी

55

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

प्रदर्श अ

class Foo:
  def __init__(self):
    self.num = 1

यह numफू उदाहरणों को बांधता है । इस क्षेत्र में परिवर्तन अन्य उदाहरणों के लिए प्रचारित नहीं किया जाता है।

इस प्रकार:

>>> foo1 = Foo()
>>> foo2 = Foo()
>>> foo1.num = 2
>>> foo2.num
1

प्रदर्शनी बी

class Bar:
  num = 1

यह numबार क्लास को बांधता है । परिवर्तन प्रचारित हैं!

>>> bar1 = Bar()
>>> bar2 = Bar()
>>> bar1.num = 2 #this creates an INSTANCE variable that HIDES the propagation
>>> bar2.num
1
>>> Bar.num = 3
>>> bar2.num
3
>>> bar1.num
2
>>> bar1.__class__.num
3

वास्तविक उत्तर

अगर मुझे एक वर्ग चर की आवश्यकता नहीं है, लेकिन केवल मेरे उदाहरण चर के लिए एक डिफ़ॉल्ट मान सेट करने की आवश्यकता है, क्या दोनों तरीके समान रूप से अच्छे हैं? या उनमें से एक दूसरे की तुलना में अधिक 'पायथोनिक' है?

प्रदर्शनी बी में कोड इसके लिए स्पष्ट गलत है: आप एकल उदाहरण के लिए वर्ग विशेषता (उदाहरण के निर्माण पर डिफ़ॉल्ट मान) को क्यों बाँधना चाहेंगे?

प्रदर्शनी ए में कोड ठीक है।

यदि आप अपने कंस्ट्रक्टर में उदाहरण चर के लिए चूक देना चाहते हैं, तो मैं यह करूंगा:

class Foo:
  def __init__(self, num = None):
    self.num = num if num is not None else 1

...या और भी:

class Foo:
  DEFAULT_NUM = 1
  def __init__(self, num = None):
    self.num = num if num is not None else DEFAULT_NUM

... या यहां तक ​​कि: (बेहतर, लेकिन अगर और केवल अगर आप अपरिवर्तनीय प्रकारों के साथ काम कर रहे हैं!)

class Foo:
  def __init__(self, num = 1):
    self.num = num

इस तरह से आप कर सकते हैं:

foo1 = Foo(4)
foo2 = Foo() #use default

1
initcore___init__underscore को इनिशियलाइज़ेशन @badp से अलग करने के लिए इस init मेथड का इस्तेमाल किया गया init मेथड है
Eliethesaiyan

पहला उदाहरण नहीं होना चाहिएdef __init__(self, num = None)
PyWalker2797

6

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

मैंने देखा है कि इस तकनीक को बहुत सफलतापूर्वक उपयोग किया गया है जो कि एक फ्रेमवर्क है जो ज़ोप के शीर्ष पर चलता है। यहां लाभ सिर्फ यह नहीं है कि जब आपकी कक्षा डेटाबेस के लिए बनी रहती है तो केवल गैर-डिफ़ॉल्ट विशेषताओं को सहेजने की आवश्यकता होती है, लेकिन यह भी जब आपको स्कीमा में एक नया फ़ील्ड जोड़ने की आवश्यकता होती है, तो सभी मौजूदा ऑब्जेक्ट नए फ़ील्ड को उसके डिफ़ॉल्ट के साथ देखते हैं। किसी भी आवश्यकता के बिना मूल्य वास्तव में संग्रहीत डेटा को बदलने के लिए।

मुझे लगता है कि यह अधिक सामान्य कोडिंग में भी अच्छा काम करता है, लेकिन यह एक स्टाइल की चीज है। जो भी आपके साथ खुशी से हो उसका प्रयोग करें।


3

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

>>> class c:
...     l = []
... 
>>> x = c()
>>> y = c()
>>> x.l
[]
>>> y.l
[]
>>> x.l.append(10)
>>> y.l
[10]
>>> c.l
[10]

केवल पहले को छोड़कर []वहाँ चारों ओर क्लोन किया गया; x.lऔर y.lदोनों दुष्ट नाम और समान तात्कालिक मूल्यों को एक ही संदर्भ से जोड़ रहे हैं। (भाषा वकील की शर्तें)
फ्लीप

1

आप वर्ग चर भी घोषित कर सकते हैं क्योंकि कोई भी ऐसा प्रचार नहीं करेगा जो प्रसार को रोकेगा। यह तब उपयोगी है जब आपको एक अच्छी तरह से परिभाषित वर्ग की आवश्यकता होती है और एट्रीब्यूटर्स को रोकना चाहते हैं। उदाहरण के लिए:

>>> class TestClass(object):
...     t = None
... 
>>> test = TestClass()
>>> test.t
>>> test2 = TestClass()
>>> test.t = 'test'
>>> test.t
'test'
>>> test2.t
>>>

इसके अलावा अगर आपको चूक की जरूरत है:

>>> class TestClassDefaults(object):
...    t = None
...    def __init__(self, t=None):
...       self.t = t
... 
>>> test = TestClassDefaults()
>>> test.t
>>> test2 = TestClassDefaults([])
>>> test2.t
[]
>>> test.t
>>>

बेशक अभी भी अन्य जवाबों में जानकारी का अनुसरण करें डिफ़ॉल्ट के रूप में उत्परिवर्ती बनाम अपरिवर्तनीय प्रकारों का उपयोग करने के बारे में __init__


0

डेटासलैस के साथ , पायथन 3.7 में एक फीचर जोड़ा गया है, अब क्लास इंस्टेंसेस पर डिफ़ॉल्ट मान प्राप्त करने के लिए एक और (काफी सुविधाजनक) तरीका है। डेकोरेटर dataclassस्वचालित रूप से आपकी कक्षा पर कुछ विधियाँ उत्पन्न करेगा, जैसे कि कंस्ट्रक्टर। जैसा कि नोटों के ऊपर प्रलेखन जुड़ा हुआ है, "[टी] वह इन उत्पन्न विधियों में उपयोग करने के लिए चर चर पीईपी 526 प्रकार की टिप्पणियों का उपयोग करके परिभाषित किया गया है "।

ओपी के उदाहरण को ध्यान में रखते हुए, हम इसे इस तरह लागू कर सकते हैं:

from dataclasses import dataclass

@dataclass
class Foo:
    num: int = 0

इस वर्ग के प्रकार की एक वस्तु का निर्माण करते समय हम वैकल्पिक रूप से मूल्य को अधिलेखित कर सकते हैं।

print('Default val: {}'.format(Foo()))
# Default val: Foo(num=0)
print('Custom val: {}'.format(Foo(num=5)))
# Custom val: Foo(num=5)
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.