हेलो, हैबर।आज हम आपको एक ऐसे लेख का अनुवाद प्रदान करते हैं जो एक ऐसे विषय पर स्पर्श करता है जो सबसे अधिक चर्चा में नहीं है: पायथन में कोड का संकलन, अर्थात्: सार वाक्यविन्यास ट्री (एएसटी) और बाइट कोड के साथ काम करते हैं। जबकि पायथन एक व्याख्या की गई भाषा है, लेकिन इसमें ऐसी विशेषताएं अनुकूलन के दृष्टिकोण से बेहद महत्वपूर्ण हैं। हम आज उनके बारे में बात करेंगे।क्या आपने कभी सोचा है कि कंपाइलर आपके कोड का अनुकूलन कैसे करता है ताकि यह तेजी से काम करे? एक सार वाक्यविन्यास वृक्ष (एएसटी) क्या है और इसका क्या उपयोग किया जा सकता है?यह समीक्षा लेख बताता है कि पायथन कोड को पेड़ के रूप (एएसटी) में कैसे बदला जाता है। अपने प्रोग्राम का एएसटी बनाने के बाद, आप अपने कोड को ऑप्टिमाइज़ करने और बदलने के अवसरों की तलाश में जा सकते हैं। हालांकि, ध्यान रखें कि पायथन कार्यक्रमों को गैर-तुच्छ तरीकों से अनुकूलित करना बेहद कठिन है ।एक पेड़ के रूप में प्रोग्राम कोड
एक कंप्यूटर कैसे सुनिश्चित कर सकता है कि वह सही क्रम में आपके कोड से अभिव्यक्तियों का मूल्यांकन करता है?ऐसा करने के लिए, वह पहले आपके प्रोग्राम कोड को एएसटी नामक एक पेड़ की संरचना में रीमेक करता है।जब एक व्याख्या की गई प्रोग्रामिंग भाषा (जैसे कि पायथन) के साथ काम किया जाता है, तो आमतौर पर यह स्वीकार किया जाता है कि दुभाषिया आपके कोड से गुजरता है और वह सब कुछ करता है जिसका वह मौके पर ही सही, पायथन कोड को किसी भी तरह से मशीन कोड में परिवर्तित किए बिना करता है। हालांकि, व्यवहार में, यह निष्पादन योजना बहुत सारी समस्याओं को उकसाती है, जो इसे बहुत असुविधाजनक बनाती है।उदाहरण के लिए, ऑपरेटरों की प्राथमिकता के रूप में ऐसी सरल समस्या। एक दृश्य अभिव्यक्ति में 3 + 4 * x
, पहले भाग की गणना की जाती है4 * x
, और उसके बाद ही गुणन के परिणाम में 3 जोड़ा जा सकता है। शायद आपने अभिव्यक्ति के तहत इन पेड़ों को खींचकर गणित के पाठों में ऑपरेटरों की प्राथमिकता सीखी:
पायथन गणितीय अंकन के मानक नियमों (पहले, गुणा, फिर जोड़) का उपयोग करता है। पायथन में, ऑपरेटरों की प्राथमिकता के साथ कुछ भी भ्रमित न करने के लिए, पहली बार में इस तरह के एक पेड़ का निर्माण किया जाता है जैसा कि पिछली तस्वीर में है। सामान्य ऑपरेशन इसके अलावा (पेड़ की जड़ पर) है, और जबकि इस राशि का बाईं ओर एक नियमित संख्या है, दाईं ओर हमारे पास उत्पाद है। परिणामी डेटा संरचना इस प्रकार दिखती है:BinOp(
left = Num(3),
op = Add(),
right = BinOp(
left = Num(4),
op = Mult(),
right = Name('x')
)
)
BinOp
बाइनरी ऑपरेशन (बाइनरी ऑपरेशन) का मतलब है और इंगित करता है कि इसके अलावा और गुणन जैसे संचालन में - दो ऑपरेंड। स्वाभाविक रूप से, अगर आपके पास अभिव्यक्ति का सही हिस्सा नहीं है, तो सही मूल्य नहीं है - इसलिए, आपको पहले से ही गुणा करना होगा।संकलक और प्रोग्रामिंग भाषाओं के सिद्धांत में, इस तरह के पेड़ को संक्षेप के लिए सार सिंटैक्स ट्री या एएसटी कहा जाता है । उपरोक्त उदाहरण में एएसटी में दो नोड्स BinOp
, दो नोड्स Num
और एक नोड शामिल हैं Name
।पायथन में एक अच्छी विशेषता है - किसी विशेष पायथन कार्यक्रम के लिए एएसटी को सीधे देखने और प्रदर्शित करने की क्षमता। एक मानक मॉड्यूल को आयात करने के लिए सभी आवश्यक हैast
, प्रोग्राम को पार्स करना, और फिर स्क्रीन पर परिणाम प्रदर्शित करना (वैसे, पार्सिंग प्रोग्राम सोर्स कोड को एएसटी ट्री में परिवर्तित करने की प्रक्रिया है)।import ast
my_tree = ast.parse("3 + 4*x")
print(ast.dump(my_tree))
हालांकि, आप देखेंगे कि पायथन द्वारा उत्पन्न एएसटी में अतिरिक्त नोड्स और फ़ील्ड होंगे, और इसे एक लाइन पर प्रदर्शित किया जाएगा, जो इसे पहली नज़र में वास्तव में की तुलना में अधिक जटिल लगता है।Module(body=[Expr(value=BinOp(left=Num(n=3), op=Add(), right=BinOp(left=Num(n=4), op=Mult(), right=Name(id='x', ctx=Load()))))])
चलो इसे पिछली बार की तरह अलग नोड्स में विभाजित करें - और एएसटी को फिर से खोलें, पहले से ही शीर्ष पर, पूरे पेड़ के हिस्से के रूप में:Module(body = [
Expr(
value = BinOp(
left = Num(n=3),
op = Add(),
right = BinOp(
left = Num(n=4),
op = Mult(),
right = Name(id='x', ctx=Load())
)
)
)
])
जाहिर है, पायथन "सोचता है" कि हमने इसे पार्स करने के लिए जो लाइन दी थी वह एक पूरा मॉड्यूल है। मॉड्यूल का शरीर इसमें निहित सभी निर्देशों की एक सूची है। हमारे उदाहरण में एकमात्र निर्देश एक अभिव्यक्ति है Expr
जिसका अर्थ वही है जो हमने ऊपर चर्चा की थी।नोट: नोड Name
में एक अतिरिक्त फ़ील्ड है ctx
(संक्षिप्त रूप में "संदर्भ"), जिसका मूल्य है Load()
। तो पायथन का कहना है कि हम चर में संग्रहीत मान का उपयोग करते हैं x
, और नाम को परिभाषित या हटाने के लिए नहीं (पुनः) x
। अब अपनी चाल, की तरह कुछ पार्स करने के लिए कोशिश del x
या खुद के x = 123
लिए, और आप कैसे क्षेत्र देखेंगे ctx
नोड में Name
किए गए परिवर्तन Del()
या Store()
क्रमश।वैसे: यदि आप मॉड्यूल स्थापित करते हैंastunparse
, तो स्क्रीन पर एएसटी आउटपुट को बहुत अधिक सुंदर बनाया जा सकता है, और यहां तक कि एएसटी को वापस लाइव पायथन कोड में परिवर्तित कर सकता है।संकलन प्रक्रिया: शेष
एएसटी कार्यक्रमों को एकत्रित करने के बाद, एएसटी के माध्यम से पूरे कार्यक्रम को पूरा करना संभव है और जिस क्रम में वे इंगित किए जाते हैं, उसी में संचालन करते हैं। हालांकि, इस दृष्टिकोण में कम से कम दो कमियां हैं। सबसे पहले, एएसटी अपेक्षाकृत बड़ी मात्रा में मेमोरी पर कब्जा कर सकता है, खासकर अगर इसमें अनावश्यक जानकारी शामिल है। दूसरे, एएसटी ट्रैवर्सल आवश्यकता से अधिक समय ले सकता है। संक्षेप में: यह किया जा सकता है, लेकिन यह अक्षम है।कंपाइलर सीधे एएसटी की प्रक्रिया नहीं करता है, लेकिन बायटेकोड तैयार करता है, जिसे फिर पायथन वर्चुअल मशीन पर निष्पादित किया जाता है। यद्यपि इस प्रक्रिया के विवरण पर चर्चा करना इस लेख के दायरे से परे है, मूल सिद्धांत यह है कि कंपाइलर एएसटी को रिवर्स पोलिश नोटेशन (आरपीएन) में बदल देता है। इसके बजाय एक ऑपरेटर डाल दिया+
बाएं और दाएं ऑपरेंड के बीच, हम इसे दोनों ऑपरेंड के बाद रखते हैं। 3 + 4*x
ऊपर के उदाहरण में , हमें अनुक्रम मिलता है 3 4 x * +
(और यह अंकन विशेष रूप से अच्छा है क्योंकि आप तुरंत अनुक्रम से देख सकते हैं: पहले आपको गुणा करने की आवश्यकता है, और उसके बाद ही जोड़)। चूंकि इस क्रम में प्रत्येक पांच तत्व सिद्धांत रूप में एक एकल बाइट के रूप में दर्शाए जा सकते हैं, ऐसे कोड को बाइट कोड कहा जाता है। पाइथन तब इस कोड को कुशलता से निष्पादित करने के लिए स्टैक्ड वर्चुअल मशीन का उपयोग करता है ।दूसरे शब्दों में, पायथन में लिखे गए कार्यक्रम को संकलित करने की प्रक्रिया दो चरणों में होती है। सबसे पहले, इनपुट द्वारा प्राप्त कार्यक्रम को पार्स किया जाता है, और परिणाम एक सार वाक्यविन्यास ट्री (एएसटी) है। संकलक तब एएसटी से गुजरता है और बायटेकोड उत्पन्न करता है। उसके बाद, पायथन दुभाषिया इस बाइटकोड को निष्पादित करता है। अनुकूलन लेने के बाद, इसे या तो एएसटी स्तर पर या बायटेकोड स्तर पर लागू किया जा सकता है। इन दोनों विकल्पों के अपने फायदे और नुकसान हैं।अंत में, ध्यान रखें कि यद्यपि किसी भी पायथन कार्यान्वयन में एएसटी आम है, एएसटी को बाईटेकोड में अनुवाद करने की प्रक्रिया अलग हो सकती है, और कुछ पायथन कार्यान्वयन में, कहते हैं, जावास्क्रिप्ट, बाइटकोड के बजाय, मध्यवर्ती स्तर पर उत्पन्न हो सकती है।अन्य प्रोग्रामिंग भाषाओं से प्रतिमान
पायथन में, जैसा कि सभी प्रोग्रामिंग भाषाएं इन्फिक्स नोटेशन का उपयोग नहीं करती हैं। इस मामले में ध्यान देने योग्य दो उदाहरण पोस्टस्क्रिप्ट हैं, जहां प्रोग्राम सीधे रिवर्स पोलिश नोटेशन में लिखा जाता है, और लिस्प, निश्चित रूप से, जहां प्रोग्राम आमतौर पर पोलिश नोटेशन में लिखे जाते हैं। तो, लिस्प में उपरोक्त उदाहरण की हमारी अभिव्यक्ति निम्नलिखित रूप लेगी (+ 3 (* 4 x))
:।एएसटी के भीतर नोड रूपांतरण
एएसटी कार्यक्रम होने के बाद, इस पेड़ के अलग-अलग हिस्सों को कैसे बदलना है? पायथन की सुविधाजनक निर्मित सुविधाओं के साथ।यदि हम एएसटी पर एक नज़र डालते हैं और उदाहरण के लिए, पाते हैं कि दोनों फ़ील्ड left
और right
नोड्स BinOp
नंबर (नोड्स Num
) हैं, तो हम पहले से संबंधित गणना कर सकते हैं, और फिर उन्हें एक BinOp
सामान्य नोड के साथ बदल सकते हैं Num
।बेशक, आपको बहुत सावधानी से कार्य करने की आवश्यकता है ताकि कार्यक्रम के व्यवहार को बदलने के लिए ऐसा परिवर्तन न करें। उदाहरण के लिए, में len([a(), b(), c(), d()])
, यह स्पष्ट है कि परिणाम 4. है लेकिन हम सभी 4 नंबर क्योंकि चार कार्य की अभिव्यक्ति को बदल नहीं सकते a
, b
, c
, d
अभी भी ठीक से लागू किया है।फिर से, एक साधारण अनुकूलन के साथ शुरू करें। जहाँ भी किसी प्रोग्राम के सोर्स कोड में नाम दिखाई देता है pi
, उसे 3.14159265 मान से बदलें। पायथन मॉड्यूल ast
पहले से ही ऐसा करने के लिए आवश्यक डेटा संरचनाएं प्रदान करता है: एक कनवर्टर वर्ग NodeTransformer
जो सभी एएसटी के माध्यम से जाता है और प्रत्येक नोड के लिए जांचता है कि क्या इसे बदला जा सकता है। डिफ़ॉल्ट रूप से, परिवर्तन विधि बस प्रत्येक नोड के लिए स्रोत नोड लौटाती है, ताकि हमें वही एएसटी मिले जिससे हमने शुरू किया था। लेकिन हम आसानी से नोड्स के लिए विधि को ओवरराइड कर सकते हैं Name
, कहते हैं, ताकि यह pi
देखने के लिए जांचें कि क्या यह है, और फिर Num
मूल नाम के साथ नोड के बजाय नोड लौटाता है ... import ast
class MyOptimizer(ast.NodeTransformer):
def visit_Name(self, node: ast.Name):
if node.id == 'pi':
return ast.Num(n=3.14159265)
return node
tree = ast.parse("y = 2 * pi")
optimizer = MyOptimizer()
tree = optimizer.visit(tree)
print(ast.dump(tree))
कनवर्टर / अनुकूलक के लिए हमारे पेड़ के माध्यम से जाने के लिए, इसकी विधि को कॉल करना आवश्यक है visit
, जो तब एक नया, बदला हुआ पेड़ वापस करेगा।दुर्भाग्य से, परिणामस्वरूप एएसटी को संकलित करना और चलाना असंभव है, इसका कारण एक तकनीकी विवरण है। यह अभी तक दिखाई नहीं दे रहा है, लेकिन (लगभग) एएसटी के सभी नोड्स में फ़ील्ड lineno
और भी हैं col_offset
। वे स्रोत कोड में किसी विशेष नोड की सटीक स्थिति का संकेत देते हैं। यदि आप उन्हें ठीक से स्थापित नहीं करते हैं, तो संकलक काम करने से इनकार कर देगा।तो, चलो स्रोत नोड Name
से नए नोड के लिए उपयुक्त फ़ील्ड की प्रतिलिपि बनाएँ Num
। तब आप परिणामस्वरूप AST को संकलित और निष्पादित कर सकते हैं:import ast
class MyOptimizer(ast.NodeTransformer):
def visit_Name(self, node: ast.Name):
if node.id == 'pi':
result = ast.Num(n=3.14159265)
result.lineno = node.lineno
result.col_offset = node.col_offset
return result
return node
tree = ast.parse("print(2 * pi)")
optimizer = MyOptimizer()
tree = optimizer.visit(tree)
code = compile(tree, "<string>", "exec")
exec(code)
नोट: समारोह संकलन न केवल स्रोत कोड की आवश्यकता है, लेकिन फ़ाइल नाम (जिसमें एक कार्यक्रम में ही, या एएसटी लाइन हो सकता है) (जैसा कि हम पूछा "<string>"
: के साथ-साथ), तीन में से एक "exec"
, "eval"
या "single"
।स्रोत कोड में नोड की स्थिति का वर्णन करने वाले क्षेत्रों की प्रतिलिपि बनाने की आवश्यकता काफी बार उठती है। इसलिए, इस उद्देश्य के लिए मॉड्यूल ast
का एक समर्पित कार्य है copy_location
, और हम लिख सकते हैं:def visit_Name(self, node: ast.Name):
if node.id == 'pi':
result = ast.Num(n=3.14159265)
return ast.copy_location(result, node)
return node
अंत में, आप पिछले उदाहरण का विस्तार कर सकते हैं ताकि यह वास्तव में नोड पर अनुकूलन का प्रदर्शन करे BinOp
। परिवर्तन नियम के अनुसार, पहले हमें बाईं ओर और फिर दाएँ नोड को BinOp के हिस्से के रूप में बदलना / अनुकूलित करना होगा। यदि परिणाम के रूप में यह पता चला है कि बाएं और दाएं दोनों नोड्स संख्याएं हैं, तो गणना को सही मौके पर किया जा सकता है और BinOp
ऑपरेशन के संख्यात्मक परिणाम के साथ मूल को बदल सकता है ।class MyVisitor(ast.NodeTransformer):
def visit_BinOp(self, node: ast.BinOp):
node.left = self.visit(node.left)
node.right = self.visit(node.right)
if isinstance(node.left, ast.Num) and isinstance(node.right, ast.Num):
if isinstance(node.op, ast.Add):
result = ast.Num(n = node.left.n + node.right.n)
return ast.copy_location(result, node)
elif isinstance(node.op, ast.Mult):
result = ast.Num(n = node.left.n * node.right.n)
return ast.copy_location(result, node)
return node
def visit_Name(self, node: ast.Name):
if node.id == 'pi':
result = ast.Num(n=3.14159265)
return ast.copy_location(result, node)
return node
tree = ast.parse("y = 2 * pi + 1")
optimizer = MyOptimizer()
tree = optimizer.visit(tree)
print(ast.dump(tree))
वैसे, सीपीथॉन कंपाइलर पहले से ही नोड्स का अनुकूलन कर रहा है BinOp
जैसा कि यहां दिखाया गया है। संबंधित कोड C में लिखा गया है और इसे Python / ast_opt.c में दिया गया है । कृपया ध्यान दें: CPython ऑप्टिमाइज़र अधिक सार्वभौमिक है और न केवल संख्याओं के साथ काम करता है, जैसा कि हम उदाहरण पर विचार कर रहे हैं, लेकिन विभिन्न प्रकार के निरंतर मूल्यों के साथ।एएसटी में नोड्स की जाँच
यह कैसे सुनिश्चित करें कि हमारे द्वारा किए गए परिवर्तन सही थे? पहले आपको एएसटी को पूरी तरह से बायपास करने और पूरे कार्यक्रम का निरीक्षण करने की आवश्यकता है।ऊपर प्रस्तुत अनुकूलक एक गंभीर दोष है। यदि आप कार्यक्रम में कहीं भी परिभाषित करते हैं तो क्या होता है pi
? बस के रूप में सरल और समझदार कुछ कल्पना करो pi = 4
। हमारा ऑप्टिमाइज़र सिर्फ़ 3.14159265 संख्यात्मक मान के साथ अभिव्यक्ति के बाईं ओर pi को बदल देगा, और पायथन इसके बाद संकलन करने से इंकार कर देगा क्योंकि यह शाब्दिक मूल्य को कुछ भी निर्दिष्ट नहीं कर सकता है।शायद यह आपके द्वारा मांगे गए व्यवहार को ठीक करता है, जिससे pi को एक सच्चा स्थिरांक बना दिया जाता है, जिसे संकलन में बदल दिया जाता है और इसे कभी भी पुन: असाइन नहीं किया जा सकता है, अर्थात यह एक अलग मान प्राप्त नहीं कर सकता है। हालांकि, यह निश्चित रूप से पायथन के शब्दार्थ का उल्लंघन करता है।तो क्या करें अगर हम पाइथन के शब्दार्थ से चिपकना चाहते हैं, लेकिन जहां भी संभव हो पाई को बदल दें? इस मामले में, आपको पहले पूरे कार्यक्रम से गुजरना होगा और जांचना होगा कि क्या मूल्य कहीं सौंपा गया है pi
। जब तक हम इसे जटिल नहीं करते हैं: हम पीआई को बदलने का सहारा नहीं लेंगे यदि कार्यक्रम में कम से कम एक बिंदु के लिए मान का कोई असाइनमेंट है pi
।अब हम ऊपर वर्णित कनवर्टर नोड के समान विज़िटर नोड का उपयोग करते हैं। कनवर्टर के विपरीत, विज़िटर किसी भी नोड को बदलने का इरादा नहीं करता है, वह बस एएसटी के माध्यम से जाता है और नोड्स की जांच करता है (उन्हें दौरा करता है)। तदनुसार, विज़िट करने के तरीके कुछ भी वापस नहीं करते हैं।हमारे मामले में, हम जांचते हैं कि क्या नोड मूल्य को लोड करने के अलावा और कुछ भी संदर्भित करता Name
है pi
या नहींpi
(संदर्भ क्षेत्र याद रखें ctx
)।import ast
class MyVisitor(ast.NodeVisitor):
def __init__(self):
self.modify_pi = False
def visit_FunctionDef(self, node: ast.FunctionDef):
if node.name == 'pi':
self.modify_pi = True
self.generic_visit(node)
def visit_Name(self, node: ast.Name):
if node.id == 'pi' and not isinstance(node.ctx, ast.Load):
self.modify_pi = True
program = """
def pi():
return 3.1415
print(2 * pi())
"""
tree = ast.parse(program)
my_visitor = MyVisitor()
my_visitor.visit(tree)
print("Pi modified:", my_visitor.modify_pi)
generic_visit(node)
विज़िटर द्वारा प्रत्येक नोड के लिए विधि को बुलाया जाता है जिसके लिए हम एक विशेष विज़िटिंग विधि प्रदान नहीं करते हैं। दूसरे शब्दों में: visit_FunctionDef
कक्षा में ऐसी कोई विधि नहीं है NodeVisitor
जिसका उपयोग करके हम कॉल कर सकें super()
। फ़ंक्शन परिभाषाओं के बारे में, हमें यह सुनिश्चित करने के लिए एक सामान्य आगंतुक को कॉल करना होगा कि फ़ंक्शन के पूरे शरीर को भी सही ढंग से संसाधित किया गया है। अन्यथा, हम फ़ंक्शन में निर्देश छिपा सकते हैं global pi
और विश्व स्तर पर मूल्य बदल सकते हैं pi
, ताकि हमारे आशावादी को कुछ भी नोटिस न हो।पायथन में स्थानीय मान
हमारी विधि, जो हमें यह निर्धारित करने की अनुमति देती है कि क्या प्रोग्रामर ने पाई को बदल दिया है, बल्कि अशिष्ट हो गया है। हालाँकि, पायथन कंपाइलर बहुत समान रूप से कार्य करता है जब यह निर्धारित करता है कि फ़ंक्शन के दायरे में कौन से नाम स्थानीय चर के अनुरूप हैं। यदि कोई चर फ़ंक्शन के दायरे में कहीं बदलता है (और स्पष्ट रूप से वैश्विक नहीं बनाया गया है, उदाहरण के लिए, वैश्विक निर्देश का उपयोग करके), तो यह चर फ़ंक्शन के पूरे दायरे में स्थानीय माना जाता है।निम्नलिखित उदाहरण चौथी पंक्ति के बिना ठीक निष्पादित करेगा। लेकिन, हालांकि x = 0
चौथी पंक्ति को कभी निष्पादित नहीं किया जाता है, फिर भी इसे एक असाइनमेंट माना जाता हैx
और इसलिए, x पूरे फ़ंक्शन के पैमाने पर एक स्थानीय चर बन जाता है, और यहां तक कि लाइन 3 पर। यही कारण है कि पायथन शपथ लेगा कि तीसरी लाइन पर चर x अभी तक कोई फर्क नहीं पड़ता।x = 1
def print_x():
print(x)
if False: x = 0
print_x()
यदि आप वास्तव में पाइथन के यहाँ काम करने के विवरण में रुचि रखते हैं, तो पाइथन / symtable.c देखें ।निष्कर्ष
पायथन में, अधिकांश प्रोग्रामिंग भाषाओं की तरह, एक विशेष कार्यक्रम को सीधे स्रोत कोड से निष्पादित नहीं किया जाता है। वास्तव में, स्रोत कोड का अनुवाद दो चरणों में होता है: पहला, एक अमूर्त सिंटैक्स ट्री (एएसटी) इससे बनाया जाता है, और फिर स्टैक्ड वर्चुअल मशीन के लिए बाइट कोड। पायथन विश्लेषण करने और यहां तक कि किसी विशेष पायथन कार्यक्रम के एएसटी को बदलने के लिए बहुत अच्छी सुविधाएँ प्रदान करता है, जिसके बाद संशोधित एएसटी को संकलित और निष्पादित किया जा सकता है। इस प्रकार, हम आसानी से अपने स्वयं के अनुकूलन को लागू कर सकते हैं।बेशक, मैंने यहाँ कई विवरणों को छोड़ दिया है। यह सुनिश्चित करने के लिए कि आपका अनुकूलन सभी संभावित मामलों और परिस्थितियों में सही ढंग से काम करेगा, एक बहुत ही गैर-तुच्छ मामला है। हालांकि, इस लेख का उद्देश्य आपको उस अनुकूलन के बारे में नहीं बताना है जो उत्पादन में उपयोग के लिए तैयार है, बल्कि इस बात का एक मूल विचार देने के लिए कि पायथन आपके प्रोग्राम कोड का विश्लेषण कैसे करता है ताकि आप सीख सकें कि इसे सही तरीके से कैसे परिवर्तित किया जाए और फिर इसे अनुकूलित करें।