वर्ग परिभाषा में एक सूची समझ से वर्ग चर तक पहुँचना


174

आप वर्ग परिभाषा के भीतर एक सूची समझ से अन्य वर्ग चर का उपयोग कैसे करते हैं? पायथन 2 में निम्नलिखित काम करता है लेकिन पायथन 3 में विफल रहता है:

class Foo:
    x = 5
    y = [x for i in range(1)]

पायथन 3.2 त्रुटि देता है:

NameError: global name 'x' is not defined

कोशिश Foo.xकरना भी काम नहीं करता है। पायथन 3 में ऐसा करने के बारे में कोई विचार?

एक और अधिक जटिल प्रेरक उदाहरण:

from collections import namedtuple
class StateDatabase:
    State = namedtuple('State', ['name', 'capital'])
    db = [State(*args) for args in [
        ['Alabama', 'Montgomery'],
        ['Alaska', 'Juneau'],
        # ...
    ]]

इस उदाहरण में, apply()एक अच्छा वर्कअराउंड होगा, लेकिन इसे पायथन 3 से दुखद रूप से हटा दिया गया है।


आपका त्रुटि संदेश गलत है। मैं NameError: global name 'x' is not definedपायथन 3.2 और 3.3 पर मिलता हूं, जो कि मैं उम्मीद करता हूं।
मार्टिन पीटर्स

दिलचस्प ... एक स्पष्ट समाधान है कि आप y को असाइन करने के बाद वर्ग परिभाषा से बाहर निकलें। Foo.y = [Foo.x for i in range (1)]
gps

3
+ martijn-pieters लिंक को डुप्लिकेट करने के लिए लिंक सही है, वहाँ स्पष्टीकरण के साथ + मैट-बी से एक टिप्पणी है: पायथन 2.7 सूची समझ का अपना नाम स्थान नहीं है (सेट या तानाशाही समझ या जनरेटर अभिव्यक्तियों के विपरीत) अपने को बदलें: [ ]}}} के साथ देखने के लिए कि कार्रवाई में)। वे सभी 3 में अपने स्वयं के नाम स्थान हैं।
gps

@ समूह: या वर्ग परिभाषा सूट में (अस्थायी) फ़ंक्शन सम्मिलित करके एक नेस्टेड गुंजाइश का उपयोग करें।
मार्टिन पीटर्स

मैंने अभी 2.7.11 को परीक्षण किया है। नाम त्रुटि
जूनो गु

जवाबों:


244

वर्ग गुंजाइश और सूची, सेट या शब्दकोश की समझ, साथ ही साथ जनरेटर के भाव मिश्रण नहीं करते हैं।

क्यों; या, इस पर आधिकारिक शब्द

पायथन 3 में, सूची की समझ को अपने स्वयं के उचित दायरे (स्थानीय नाम स्थान) को दिया गया था, ताकि उनके स्थानीय चर आसपास के दायरे में अधिक से अधिक रक्तस्राव को रोक सकें (देखें कि पायथन सूची समझ विद्रोही नामों को समझ के दायरे के बाद भी है? क्या यह सही है? )। मॉड्यूल या किसी फ़ंक्शन में ऐसी सूची की समझ का उपयोग करते समय यह बहुत अच्छा है, लेकिन कक्षाओं में, स्कूपिंग थोड़ा, उह, अजीब है

यह पेप 227 में प्रलेखित है :

वर्ग दायरे में नाम सुलभ नहीं हैं। फ़ंक्शन के दायरे को घेरने वाले अंतरतम में नाम हल किए जाते हैं। यदि नेस्टेड स्कोप की एक श्रृंखला में एक वर्ग परिभाषा होती है, तो रिज़ॉल्यूशन प्रक्रिया कक्षा की परिभाषा को छोड़ देती है।

और classयौगिक विवरण दस्तावेज में :

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

जोर मेरा; निष्पादन फ्रेम अस्थायी क्षेत्र है।

क्योंकि गुंजाइश को एक वर्ग वस्तु पर विशेषताओं के रूप में पुनर्निर्मित किया जाता है, जिससे इसे गैर-स्कोप दायरे के रूप में उपयोग करने की अनुमति मिलती है और साथ ही अपरिभाषित व्यवहार होता है; क्या होगा यदि एक वर्ग विधि को xनेस्टेड स्कोप वैरिएबल के रूप में संदर्भित किया जाता है, तो Foo.xउदाहरण के लिए, साथ ही साथ हेरफेर करता है? इससे भी महत्वपूर्ण बात, उपवर्गों के लिए इसका क्या अर्थ होगा Foo? पायथन को एक वर्ग गुंजाइश को अलग तरीके से व्यवहार करना पड़ता है क्योंकि यह एक कार्यक्षेत्र से बहुत अलग है।

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

एक वर्ग ब्लॉक में परिभाषित नामों का दायरा वर्ग ब्लॉक तक सीमित है; यह विधियों के कोड ब्लॉकों तक नहीं फैलता है - इसमें एक कार्यक्षेत्र का उपयोग करके लागू किए जाने के बाद से समझ और जनरेटर अभिव्यक्तियाँ शामिल हैं। इसका मतलब है कि निम्नलिखित विफल हो जाएगा:

class A:
     a = 42
     b = list(a + i for i in range(10))

इसलिए, संक्षेप में: आप उस दायरे में संलग्न कार्यों, सूची समझ या जनरेटर अभिव्यक्तियों से वर्ग गुंजाइश का उपयोग नहीं कर सकते हैं; वे ऐसे कार्य करते हैं जैसे कि वह क्षेत्र मौजूद नहीं है। पायथन 2 में, शॉर्टकट का उपयोग करके सूची समझ को लागू किया गया था, लेकिन पायथन 3 में उन्हें अपना स्वयं का कार्यक्षेत्र मिला (जैसा कि उन्हें सभी के साथ होना चाहिए था) और इस प्रकार आपका उदाहरण टूट जाता है। पायथन संस्करण की परवाह किए बिना अन्य समझ के प्रकारों का अपना दायरा है, इसलिए पायथन 2 में एक सेट या तानाशाही समझ के साथ एक समान उदाहरण टूट जाएगा।

# Same error, in Python 2 or 3
y = {x: x for i in range(1)}

(छोटा) अपवाद; या, क्यों एक हिस्सा अभी भी काम कर सकता है

पायथन संस्करण की परवाह किए बिना आसपास के दायरे में निष्पादित होने वाली समझ या जनरेटर अभिव्यक्ति का एक हिस्सा है। यह सबसे बाहरी पुनरावृत्ति के लिए अभिव्यक्ति होगी। आपके उदाहरण में, यह है range(1):

y = [x for i in range(1)]
#               ^^^^^^^^

इस प्रकार, xउस अभिव्यक्ति का उपयोग करने से त्रुटि नहीं होगी:

# Runs fine
y = [i for i in range(x)]

यह केवल सबसे बाहरी पुनरावृत्ति पर लागू होता है; यदि एक समझ के कई forखंड हैं, तो आंतरिक forखंड के पुनरावृत्तियों का मूल्यांकन समझ के दायरे में किया जाता है:

# NameError
y = [i for i in range(1) for j in range(x)]

जनरेटर के अभिव्यक्ति की सबसे बाहरी चलने योग्य बनाने के दौरान यह डिज़ाइन निर्णय जीनएक्सपी सृजन समय पर एक त्रुटि को फेंकने के लिए किया गया था, जब एक त्रुटि फेंकता है, या जब सबसे बाहरी पुनरावृत्ति चलने योग्य नहीं होती है। समझदारी इस व्यवहार को संगति के लिए साझा करती है।

हुड के नीचे देख; या, जिस तरह से आप चाहते थे उससे अधिक विस्तार

आप disमॉड्यूल का उपयोग करके यह सब देख सकते हैं । मैं निम्नलिखित उदाहरणों में पायथन 3.3 का उपयोग कर रहा हूं, क्योंकि यह योग्य नाम जोड़ता है जो बड़े करीने से उन कोड ऑब्जेक्ट्स को पहचानते हैं जिन्हें हम निरीक्षण करना चाहते हैं। उत्पादित बायटेकोड अन्यथा कार्यात्मक रूप से पायथन 3.2 के समान है।

एक कक्षा बनाने के लिए , पायथन अनिवार्य रूप से पूरे सुइट को लेता है जो क्लास बॉडी बनाता है (इसलिए सब कुछ class <name>:लाइन से एक स्तर गहरा हो जाता है ), और निष्पादित करता है जैसे कि यह एक फ़ंक्शन था:

>>> import dis
>>> def foo():
...     class Foo:
...         x = 5
...         y = [x for i in range(1)]
...     return Foo
... 
>>> dis.dis(foo)
  2           0 LOAD_BUILD_CLASS     
              1 LOAD_CONST               1 (<code object Foo at 0x10a436030, file "<stdin>", line 2>) 
              4 LOAD_CONST               2 ('Foo') 
              7 MAKE_FUNCTION            0 
             10 LOAD_CONST               2 ('Foo') 
             13 CALL_FUNCTION            2 (2 positional, 0 keyword pair) 
             16 STORE_FAST               0 (Foo) 

  5          19 LOAD_FAST                0 (Foo) 
             22 RETURN_VALUE         

पहले LOAD_CONSTवहां Fooक्लास बॉडी के लिए एक कोड ऑब्जेक्ट लोड होता है, फिर उसे एक फंक्शन में बनाता है, और उसे कॉल करता है। परिणाम है कि कॉल की तो, वर्ग के नाम स्थान बनाने के लिए इसके प्रयोग किया जाता है __dict__। अब तक सब ठीक है।

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

यहाँ याद रखने वाली महत्वपूर्ण बात यह है कि संकलन के समय पायथन इन संरचनाओं का निर्माण करता है; classसुइट में एक कोड वस्तु (है <code object Foo at 0x10a436030, file "<stdin>", line 2>) कि पहले से ही संकलित किया गया है।

आइए उस कोड ऑब्जेक्ट का निरीक्षण करें जो क्लास बॉडी स्वयं बनाता है; कोड ऑब्जेक्ट में एक co_constsसंरचना होती है:

>>> foo.__code__.co_consts
(None, <code object Foo at 0x10a436030, file "<stdin>", line 2>, 'Foo')
>>> dis.dis(foo.__code__.co_consts[1])
  2           0 LOAD_FAST                0 (__locals__) 
              3 STORE_LOCALS         
              4 LOAD_NAME                0 (__name__) 
              7 STORE_NAME               1 (__module__) 
             10 LOAD_CONST               0 ('foo.<locals>.Foo') 
             13 STORE_NAME               2 (__qualname__) 

  3          16 LOAD_CONST               1 (5) 
             19 STORE_NAME               3 (x) 

  4          22 LOAD_CONST               2 (<code object <listcomp> at 0x10a385420, file "<stdin>", line 4>) 
             25 LOAD_CONST               3 ('foo.<locals>.Foo.<listcomp>') 
             28 MAKE_FUNCTION            0 
             31 LOAD_NAME                4 (range) 
             34 LOAD_CONST               4 (1) 
             37 CALL_FUNCTION            1 (1 positional, 0 keyword pair) 
             40 GET_ITER             
             41 CALL_FUNCTION            1 (1 positional, 0 keyword pair) 
             44 STORE_NAME               5 (y) 
             47 LOAD_CONST               5 (None) 
             50 RETURN_VALUE         

उपरोक्त बायोटेक क्लास बॉडी बनाता है। फ़ंक्शन को निष्पादित किया जाता है और परिणामी locals()नाम स्थान, युक्त होता है xऔर yक्लास बनाने के लिए उपयोग किया जाता है (सिवाय इसके कि यह काम नहीं करता है क्योंकि xइसे वैश्विक के रूप में परिभाषित नहीं किया गया है)। ध्यान दें कि भंडारण के बाद 5में x, यह एक कोड वस्तु को लोड करता है; यह सूची की समझ है; यह एक फ़ंक्शन ऑब्जेक्ट में उसी तरह लपेटा जाता है जैसे क्लास बॉडी थी; बनाया गया कार्य एक range(1)स्थैतिक तर्क लेता है, अपने लूपिंग कोड के लिए उपयोग करने के लिए पुनरावृति , एक पुनरावृत्ति करने के लिए डाली जाती है। जैसा कि बाइटकोड में दिखाया गया है, range(1)कक्षा के दायरे में मूल्यांकन किया जाता है।

इससे आप देख सकते हैं कि फ़ंक्शन या जनरेटर के लिए कोड ऑब्जेक्ट के बीच एकमात्र अंतर, और एक समझ के लिए कोड ऑब्जेक्ट यह है कि उत्तरार्द्ध को तुरंत निष्पादित किया जाता है जब मूल कोड ऑब्जेक्ट निष्पादित होता है; bytecode बस मक्खी पर एक समारोह बनाता है और इसे कुछ छोटे चरणों में निष्पादित करता है।

पाइथन 2.x इनलाइन बाइटकोड का उपयोग करता है इसके बजाय, यहाँ पाइथन 2.7 से आउटपुट है:

  2           0 LOAD_NAME                0 (__name__)
              3 STORE_NAME               1 (__module__)

  3           6 LOAD_CONST               0 (5)
              9 STORE_NAME               2 (x)

  4          12 BUILD_LIST               0
             15 LOAD_NAME                3 (range)
             18 LOAD_CONST               1 (1)
             21 CALL_FUNCTION            1
             24 GET_ITER            
        >>   25 FOR_ITER                12 (to 40)
             28 STORE_NAME               4 (i)
             31 LOAD_NAME                2 (x)
             34 LIST_APPEND              2
             37 JUMP_ABSOLUTE           25
        >>   40 STORE_NAME               5 (y)
             43 LOAD_LOCALS         
             44 RETURN_VALUE        

कोई कोड ऑब्जेक्ट लोड नहीं किया जाता है, इसके बजाय एक FOR_ITERलूप इनलाइन चलाया जाता है। तो पायथन 3.x में, सूची जनरेटर को अपने स्वयं के एक उचित कोड ऑब्जेक्ट दिया गया था, जिसका अर्थ है कि इसका अपना दायरा है।

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

>>> foo.__code__.co_consts[1].co_consts
('foo.<locals>.Foo', 5, <code object <listcomp> at 0x10a385420, file "<stdin>", line 4>, 'foo.<locals>.Foo.<listcomp>', 1, None)
>>> dis.dis(foo.__code__.co_consts[1].co_consts[2])
  4           0 BUILD_LIST               0 
              3 LOAD_FAST                0 (.0) 
        >>    6 FOR_ITER                12 (to 21) 
              9 STORE_FAST               1 (i) 
             12 LOAD_GLOBAL              0 (x) 
             15 LIST_APPEND              2 
             18 JUMP_ABSOLUTE            6 
        >>   21 RETURN_VALUE         

बायटेकोड का यह हिस्सा पहले ( range(1)इटेरेटर) में पास किए गए पहले तर्क को लोड करता है , और जैसे पायथन 2.x संस्करण FOR_ITERइसके ऊपर लूप करने और इसके आउटपुट को बनाने के लिए उपयोग करता है।

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

>>> def foo():
...     x = 2
...     class Foo:
...         x = 5
...         y = [x for i in range(1)]
...     return Foo
... 
>>> dis.dis(foo.__code__.co_consts[2].co_consts[2])
  5           0 BUILD_LIST               0 
              3 LOAD_FAST                0 (.0) 
        >>    6 FOR_ITER                12 (to 21) 
              9 STORE_FAST               1 (i) 
             12 LOAD_DEREF               0 (x) 
             15 LIST_APPEND              2 
             18 JUMP_ABSOLUTE            6 
        >>   21 RETURN_VALUE         

LOAD_DEREFपरोक्ष रूप से लोड होगा xकोड वस्तु सेल वस्तुओं से:

>>> foo.__code__.co_cellvars               # foo function `x`
('x',)
>>> foo.__code__.co_consts[2].co_cellvars  # Foo class, no cell variables
()
>>> foo.__code__.co_consts[2].co_consts[2].co_freevars  # Refers to `x` in foo
('x',)
>>> foo().y
[2]

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

>>> def spam(x):
...     def eggs():
...         return x
...     return eggs
... 
>>> spam(1).__code__.co_freevars
('x',)
>>> spam(1)()
1
>>> spam(1).__closure__
>>> spam(1).__closure__[0].cell_contents
1
>>> spam(5).__closure__[0].cell_contents
5

इसलिए, संक्षेप में:

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

एक वर्कअराउंड; या, इसके बारे में क्या करना है

यदि आप xचर के लिए एक स्पष्ट गुंजाइश बनाने के लिए थे , जैसे किसी फ़ंक्शन में, आप सूची समझने के लिए वर्ग-गुंजाइश चर का उपयोग कर सकते हैं:

>>> class Foo:
...     x = 5
...     def y(x):
...         return [x for i in range(1)]
...     y = y(x)
... 
>>> Foo.y
[5]

'अस्थायी' yफ़ंक्शन को सीधे कहा जा सकता है; जब हम इसके वापसी मूल्य के साथ करते हैं तो हम इसे बदल देते हैं। इसके दायरे है जब हल करने पर विचार x:

>>> foo.__code__.co_consts[1].co_consts[2]
<code object y at 0x10a5df5d0, file "<stdin>", line 4>
>>> foo.__code__.co_consts[1].co_consts[2].co_cellvars
('x',)

बेशक, आपके कोड को पढ़ने वाले लोग इस पर अपने सिर को थोड़ा खरोंच देंगे; आप यह बताने के लिए कि आप ऐसा क्यों कर रहे हैं, एक बड़ी मोटी टिप्पणी करना चाहते हैं।

सबसे अच्छा काम-के आसपास सिर्फ __init__एक उदाहरण चर बनाने के लिए उपयोग करने के लिए है:

def __init__(self):
    self.y = [self.x for i in range(1)]

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

from collections import namedtuple
State = namedtuple('State', ['name', 'capital'])

class StateDatabase:
    db = [State(*args) for args in [
       ('Alabama', 'Montgomery'),
       ('Alaska', 'Juneau'),
       # ...
    ]]

21
आप बंधन को ठीक करने के लिए एक लैम्ब्डा का उपयोग भी कर सकते हैं:y = (lambda x=x: [x for i in range(1)])()
ecatmur

3
@ecatmur: बिल्कुल, lambdaकेवल अनाम कार्य हैं, सब के बाद।
मार्टिन पीटर्स

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

9
यदि यह समझाने के लिए तकनीकी जानकारी के एक पृष्ठ की आवश्यकता है कि क्यों कुछ सहज रूप से काम नहीं करता है, तो मैं उस बग को कॉल करता हूं।
जोनाथन

5
@JonathanLeaders: इसे बग न कहें , इसे ट्रेडऑफ़ कहें । यदि आप ए और बी चाहते हैं, लेकिन उनमें से केवल एक ही प्राप्त कर सकते हैं, तो कोई फर्क नहीं पड़ता कि आप कैसे तय करते हैं, कुछ स्थितियों में आप परिणाम को नापसंद करेंगे। यही ज़िन्दगी है।
लुत्ज प्रेचल

15

मेरी राय में यह पायथन 3 में एक दोष है। मुझे उम्मीद है कि वे इसे बदल देंगे।

ओल्ड वे (2.7 में काम करता है, NameError: name 'x' is not defined3+ में फेंकता है):

class A:
    x = 4
    y = [x+i for i in range(1)]

ध्यान दें: बस इसे स्कूप A.xकरने से इसका समाधान नहीं होगा

नया तरीका (3+ में काम करता है):

class A:
    x = 4
    y = (lambda x=x: [x+i for i in range(1)])()

क्योंकि वाक्य रचना इतनी बदसूरत है कि मैं कंस्ट्रक्टर में आमतौर पर अपने सभी वर्ग चर को प्रारंभिक करता हूं


6
यह समस्या पायथन 2 में भी मौजूद है, जब जनरेटर के भावों का उपयोग किया जाता है, साथ ही सेट और शब्दकोश की समझ भी। यह बग नहीं है, यह एक परिणाम है कि क्लास नेमस्पेस कैसे काम करता है। यह नहीं बदलेगा।
मार्टिन पीटर्स

4
और मैं ध्यान देता हूं कि आपका वर्कअराउंड वही करता है जो मेरा जवाब पहले से ही बताता है: एक नया दायरा बनाएं (एक लैंबडा defफ़ंक्शन बनाने के लिए उपयोग करने से अलग नहीं है)।
मार्टिन पीटर्स

1
हां। हालांकि, एक नज़र में काम के इर्द-गिर्द जवाब देना अच्छा होता है, यह एक व्यवहार को बग के रूप में बताता है, जब यह भाषा के काम करने के तरीके का एक साइड-इफ़ेक्ट होता है (और इसलिए इसे बदला नहीं जाएगा)
jsbueno

यह एक अलग समस्या है, जो वास्तव में अजगर 3 में कोई समस्या नहीं है। यह केवल आईपीथॉन में होता है जब आप इसे कहने के लिए एम्बेड मोड में कहते हैं python -c "import IPython;IPython.embed()"। IPython को सीधे कहें ipythonऔर समस्या गायब हो जाएगी।
रियाज रिजवी

6

स्वीकृत उत्तर उत्कृष्ट जानकारी प्रदान करता है, लेकिन यहां कुछ अन्य झुर्रियां दिखाई देती हैं - सूची समझ और जनरेटर अभिव्यक्तियों के बीच अंतर। एक डेमो जो मैंने साथ खेला है:

class Foo:

    # A class-level variable.
    X = 10

    # I can use that variable to define another class-level variable.
    Y = sum((X, X))

    # Works in Python 2, but not 3.
    # In Python 3, list comprehensions were given their own scope.
    try:
        Z1 = sum([X for _ in range(3)])
    except NameError:
        Z1 = None

    # Fails in both.
    # Apparently, generator expressions (that's what the entire argument
    # to sum() is) did have their own scope even in Python 2.
    try:
        Z2 = sum(X for _ in range(3))
    except NameError:
        Z2 = None

    # Workaround: put the computation in lambda or def.
    compute_z3 = lambda val: sum(val for _ in range(3))

    # Then use that function.
    Z3 = compute_z3(X)

    # Also worth noting: here I can refer to XS in the for-part of the
    # generator expression (Z4 works), but I cannot refer to XS in the
    # inner-part of the generator expression (Z5 fails).
    XS = [15, 15, 15, 15]
    Z4 = sum(val for val in XS)
    try:
        Z5 = sum(XS[i] for i in range(len(XS)))
    except NameError:
        Z5 = None

print(Foo.Z1, Foo.Z2, Foo.Z3, Foo.Z4, Foo.Z5)

2

यह पायथन में एक बग है। समझ को विज्ञापन के रूप में छोरों के लिए बराबर माना जाता है, लेकिन कक्षाओं में यह सच नहीं है। कम से कम पायथन 3.6.6 तक, एक वर्ग में उपयोग किए जाने वाले एक समझ में, समझ के बाहर से केवल एक चर समझ के अंदर पहुंच योग्य है, और इसका उपयोग सबसे बाहरी पुनरावृत्तिकर्ता के रूप में किया जाना चाहिए। किसी फ़ंक्शन में, यह गुंजाइश सीमा लागू नहीं होती है।

यह समझने के लिए कि यह बग क्यों है, आइए मूल उदाहरण पर लौटते हैं। यह विफल रहता है:

class Foo:
    x = 5
    y = [x for i in range(1)]

लेकिन यह काम करता है:

def Foo():
    x = 5
    y = [x for i in range(1)]

संदर्भ गाइड में इस खंड के अंत में सीमा को बताया गया है ।


1

चूंकि बाहरी क्षेत्र का मूल्यांकन आसपास के दायरे में किया जाता है, इसलिए हम निर्भरता के दायरे पर निर्भरता को ले जाने के लिए zipएक साथ उपयोग कर सकते हैं itertools.repeat:

import itertools as it

class Foo:
    x = 5
    y = [j for i, j in zip(range(3), it.repeat(x))]

एक भी forसमझ में आ गए छोरों का उपयोग कर सकते हैं और सबसे बाहरी पुनरावृत्ति में निर्भरता को शामिल कर सकते हैं :

class Foo:
    x = 5
    y = [j for j in (x,) for i in range(3)]

ओपी के विशिष्ट उदाहरण के लिए:

from collections import namedtuple
import itertools as it

class StateDatabase:
    State = namedtuple('State', ['name', 'capital'])
    db = [State(*args) for State, args in zip(it.repeat(State), [
        ['Alabama', 'Montgomery'],
        ['Alaska', 'Juneau'],
        # ...
    ])]
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.