वर्ग गुंजाइश और सूची, सेट या शब्दकोश की समझ, साथ ही साथ जनरेटर के भाव मिश्रण नहीं करते हैं।
क्यों; या, इस पर आधिकारिक शब्द
पायथन 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'),
# ...
]]
NameError: global name 'x' is not defined
पायथन 3.2 और 3.3 पर मिलता हूं, जो कि मैं उम्मीद करता हूं।