अरबवें समय के लिए सापेक्ष आयात


716

मैं यहाँ आ चूका हूँ:

और बहुत सारे URL जिन्हें मैंने कॉपी नहीं किया था, कुछ SO पर, कुछ अन्य साइटों पर, जब मुझे लगा कि मेरे पास समाधान जल्दी होगा।

हमेशा के लिए आवर्ती प्रश्न यह है: विंडोज 7, 32-बिट पायथन 2.7.3 के साथ, मैं इसे "गैर-पैकेज में रिश्तेदार आयात" संदेश को कैसे हल करूं? मैंने pep-0328 पर पैकेज की एक सटीक प्रतिकृति बनाई:

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
        moduleY.py
    subpackage2/
        __init__.py
        moduleZ.py
    moduleA.py

आयात कंसोल से किया गया था।

मैंने उनके उपयुक्त मॉड्यूल्स में स्पैम और अंडे नामक कार्य किए हैं। स्वाभाविक रूप से, यह काम नहीं किया। उत्तर जाहिरा तौर पर 4 वें URL में सूचीबद्ध है, लेकिन यह मेरे लिए पूर्व छात्र है। मेरे द्वारा देखे गए URL में से एक पर यह प्रतिक्रिया थी:

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

उपरोक्त प्रतिक्रिया आशाजनक लग रही है, लेकिन यह मेरे लिए सभी चित्रलिपि है। तो मेरा सवाल है, मैं पायथन कैसे बनाऊं जो मुझे "गैर-पैकेज में रिश्तेदार आयात करने का प्रयास नहीं करता"? एक उत्तर है जिसमें शामिल है -m, माना जाता है।

क्या कोई मुझे बता सकता है कि पायथन उस त्रुटि संदेश को क्यों देता है, "नॉन-पैकेज" से इसका क्या मतलब है, आप एक 'पैकेज' को क्यों और कैसे परिभाषित करते हैं, और सटीक उत्तर एक किंडरगार्टनर के लिए समझने में आसान है


5
आपके द्वारा दिखाए गए फ़ाइलों का उपयोग करने का प्रयास कैसे कर रहे हैं? आप जो कोड चला रहे हैं, वह क्या है?
ब्रेनबार

Python.org/dev/peps/pep-0328 देखें । मैंने अपने पोस्ट में वर्णित पैकेज प्रारूप का उपयोग किया। Init .py फ़ाइलों खाली हैं। मॉड्यूलY.py है def spam(): pass, मॉड्यूलA.py है def eggs(): pass। मैंने एक जोड़ी को "से आयात करने की कोशिश की। कुछ आयात करें" आदेश, लेकिन उन्होंने काम नहीं किया। फिर से, देखें पेप -0328।

6
मेरा जवाब देखिए। आपने अभी भी पूरी तरह से स्पष्ट नहीं किया है कि आप क्या कर रहे हैं, लेकिन यदि आप from .something import somethingइंटरएक्टिव इंटरप्रेटर में करने की कोशिश कर रहे हैं, तो यह काम नहीं करेगा। सापेक्ष आयात केवल मॉड्यूल के भीतर ही किया जा सकता है, अंतःक्रियात्मक रूप से नहीं।
ब्रेनबार

105
इस तथ्य के रूप में "बिलियन" लोगों का एकमात्र तथ्य - ठीक 83,136 - इस सवाल का पता लगाने के लिए आयात के साथ पर्याप्त कठिनाई हो रही है; हम केवल यह निष्कर्ष निकाल सकते हैं कि अजगर आयात बहुत से लोगों के लिए काउंटर-सहज ज्ञान युक्त हैं, यदि अधिकांश प्रोग्रामर नहीं हैं। गुइडो, शायद आपको इसे स्वीकार करना चाहिए और आयात तंत्र को फिर से डिज़ाइन करने के लिए एक समिति के लिए पूछना चाहिए। कम से कम, इस सिंटैक्स को काम करना चाहिए अगर x निर्देशिका और z.py एक ही निर्देशिका में हैं। अर्थात् यदि x.py का कथन है, "से .z आयात MyZebraClass" x को z EVEN को आयात करना चाहिए यदि इसे मुख्य रूप से चलाया जा रहा है ! इतना कठिन क्यों है?
स्टीव एल।

4
इस धागे के बहुत से पढ़ने के बाद, हालांकि इस सवाल का जवाब नहीं है, "बस पूर्ण आयात का उपयोग करें" समाधान लगता है ...
कोडजॉकी

जवाबों:


1043

स्क्रिप्ट बनाम मॉड्यूल

यहाँ एक स्पष्टीकरण है। संक्षिप्त संस्करण यह है कि पायथन फ़ाइल को सीधे चलाने और उस फ़ाइल को कहीं और से आयात करने के बीच एक बड़ा अंतर है। बस यह जानना कि कोई फ़ाइल किस डाइरेक्टरी में है, यह निर्धारित नहीं करता है कि पायथन किस पैकेज में है। यह निर्भर करता है, इसके अलावा, आप फ़ाइल को पायथन में कैसे लोड करते हैं (रन करके या आयात करके)।

पायथन फ़ाइल को लोड करने के दो तरीके हैं: शीर्ष-स्तरीय स्क्रिप्ट के रूप में, या एक मॉड्यूल के रूप में। एक फ़ाइल को शीर्ष-स्तरीय स्क्रिप्ट के रूप में लोड किया जाता है यदि आप इसे सीधे निष्पादित करते हैं, उदाहरण के python myfile.pyलिए कमांड लाइन पर टाइप करके । यह एक मॉड्यूल के रूप में लोड किया जाता है यदि आप करते हैं python -m myfile, या यदि importकिसी अन्य फ़ाइल के अंदर एक बयान का सामना करने पर इसे लोड किया जाता है। एक समय में केवल एक शीर्ष-स्तरीय स्क्रिप्ट हो सकती है; शीर्ष-स्तरीय स्क्रिप्ट पायथन फ़ाइल है जिसे आप चीजों को शुरू करने के लिए चलाते थे।

नामकरण

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

उदाहरण के लिए आपके उदाहरण में:

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
    moduleA.py

यदि आप आयात करते हैं moduleX(नोट: आयातित , सीधे निष्पादित नहीं), तो इसका नाम होगा package.subpackage1.moduleX। यदि आप आयात करते हैं moduleA, तो इसका नाम होगा package.moduleA। हालाँकि, यदि आप सीधे moduleX कमांड लाइन से चलते हैं , तो इसका नाम बदले में होगा __main__, और यदि आप सीधे moduleAकमांड लाइन से चलते हैं , तो इसका नाम होगा __main__। जब एक मॉड्यूल को शीर्ष-स्तरीय स्क्रिप्ट के रूप में चलाया जाता है, तो यह अपना सामान्य नाम खो देता है और इसके बजाय इसका नाम होता है __main__

अपने पैकेज युक्त मॉड्यूल के माध्यम से प्रवेश नहीं

एक अतिरिक्त शिकन है: मॉड्यूल का नाम इस बात पर निर्भर करता है कि इसे उस निर्देशिका से "सीधे" आयात किया गया था या पैकेज के माध्यम से आयात किया गया था। इससे केवल तभी फर्क पड़ता है जब आप किसी निर्देशिका में पायथन चलाते हैं, और उसी निर्देशिका में फ़ाइल आयात करने की कोशिश करते हैं (या इसका एक उपनिर्देशिका)। उदाहरण के लिए, यदि आप निर्देशिका में पायथन दुभाषिया शुरू करते हैं package/subpackage1और फिर करते हैं import moduleX, तो moduleXसिर्फ नाम का नाम होगा moduleX, और नहीं package.subpackage1.moduleX। ऐसा इसलिए है क्योंकि पायथन वर्तमान निर्देशिका को स्टार्टअप पर अपने खोज पथ में जोड़ता है; यदि यह वर्तमान निर्देशिका में इन-टू-इम्पोर्टेड मॉड्यूल पाता है, तो यह नहीं पता होगा कि वह निर्देशिका पैकेज का हिस्सा है, और पैकेज जानकारी मॉड्यूल के नाम का हिस्सा नहीं बनेगी।

एक विशेष मामला यह है कि यदि आप इंटरप्रेटर को अंतःक्रियात्मक रूप से चलाते हैं (जैसे, बस टाइप करें pythonऔर मक्खी पर पायथन कोड दर्ज करना शुरू करें)। इस मामले में उस संवादात्मक सत्र का नाम है __main__

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

अब आप अपने प्रश्न में शामिल उद्धरण को देखें:

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

सापेक्ष आयात ...

सापेक्ष आयात मॉड्यूल के नाम का उपयोग यह निर्धारित करने के लिए करता है कि यह पैकेज में कहां है। जब आप किसी सापेक्ष आयात का उपयोग करते हैं from .. import foo, तो डॉट्स पैकेज पदानुक्रम में कुछ स्तरों को बढ़ाने का संकेत देते हैं। उदाहरण के लिए, यदि आपके वर्तमान मॉड्यूल का नाम है package.subpackage1.moduleX, तो ..moduleAइसका मतलब होगा package.moduleA। एक के लिए from .. importकाम करने के लिए, मॉड्यूल के नाम के रूप में वहाँ में हैं कई बिंदुओं के रूप में कम से कम होनी चाहिए importबयान।

... केवल एक पैकेज में रिश्तेदार हैं

हालांकि, यदि आपके मॉड्यूल का नाम है __main__, तो इसे पैकेज में नहीं माना जाता है। इसके नाम में कोई बिंदु नहीं है, और इसलिए आप from .. importइसके अंदर कथनों का उपयोग नहीं कर सकते । यदि आप ऐसा करने की कोशिश करते हैं, तो आपको "गैर-पैकेज में रिश्तेदार-आयात" त्रुटि मिलेगी।

लिपियां सापेक्ष आयात नहीं कर सकती हैं

आपने शायद जो किया था, moduleXउसे कमांड लाइन से चलाने या पसंद करने की कोशिश की गई है । जब आपने ऐसा किया था, तो इसका नाम सेट किया गया था __main__, जिसका अर्थ है कि इसके भीतर सापेक्ष आयात विफल हो जाएगा, क्योंकि इसका नाम प्रकट नहीं करता है कि यह एक पैकेज में है। ध्यान दें कि यह तब भी होगा जब आप उसी निर्देशिका से पायथन चलाते हैं जहां एक मॉड्यूल है, और फिर उस मॉड्यूल को आयात करने का प्रयास करें, क्योंकि, जैसा कि ऊपर वर्णित है, पायथन को मौजूदा निर्देशिका में मॉड्यूल को "बहुत जल्दी" पता चलेगा, बिना यह एहसास किए एक पैकेज का हिस्सा।

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

दो समाधान:

  1. यदि आप वास्तव में moduleXसीधे भागना चाहते हैं, लेकिन आप अभी भी चाहते हैं कि इसे एक पैकेज का हिस्सा माना जाए, तो आप कर सकते हैं python -m package.subpackage1.moduleX-mअजगर बताता नहीं उच्च-स्तरीय स्क्रिप्ट के रूप में, एक मॉड्यूल के रूप में यह लोड करने के लिए।

  2. या शायद आप वास्तव में चलाना नहीं चाहते हैंmoduleX , आप बस कुछ अन्य स्क्रिप्ट को चलाना चाहते हैं, कहते हैं myfile.py, कि अंदर कार्यों का उपयोग करता है moduleX। अगर ऐसा है, तो myfile.py कहीं और डालें - निर्देशिका के अंदर नहींpackage - और इसे चलाएं। अगर आप अंदर की myfile.pyचीजों को पसंद करते हैं from package.moduleA import spam, तो यह ठीक काम करेगा।

टिप्पणियाँ

  • या तो इन समाधानों के लिए, पैकेज निर्देशिका ( packageआपके उदाहरण में) पायथन मॉड्यूल खोज पथ ( sys.path) से सुलभ होनी चाहिए । यदि यह नहीं है, तो आप पैकेज में किसी भी चीज़ का उपयोग मज़बूती से नहीं कर पाएंगे।

  • पायथन 2.6 के बाद से, पैकेज-रिज़ॉल्यूशन उद्देश्यों के लिए मॉड्यूल का "नाम" न केवल इसकी __name__विशेषताओं से निर्धारित होता है, बल्कि विशेषता द्वारा भी __package__। इसलिए मैं __name__मॉड्यूल के "नाम" का उल्लेख करने के लिए स्पष्ट प्रतीक का उपयोग करने से बच रहा हूं । चूंकि पायथन 2.6 एक मॉड्यूल का "नाम" प्रभावी रूप से है __package__ + '.' + __name__, या बस __name__अगर __package__है None।)


62
Its name has no dots, and therefore you cannot use from .. import statements inside it. If you try to do so, you will get the "relative-import in non-package" error.यह मौलिक रूप से परेशान करने वाला है। वर्तमान निर्देशिका को देखने में क्या मुश्किल है? अजगर को इसके लिए सक्षम होना चाहिए। क्या यह संस्करण 3x में तय किया गया है?

7
@Stopforgettingmyaccounts ...: PEP 366 दिखाता है कि कैसे काम करता है। किसी फ़ाइल के अंदर, आप कर सकते हैं __package__ = 'package.subpackage1'या पसंद कर सकते हैं । फिर उस फ़ाइल को हमेशा उस पैकेज का हिस्सा माना जाएगा, भले ही सीधे चला हो। यदि आपके पास अन्य प्रश्न हैं, __package__तो आप एक अलग प्रश्न पूछना चाहते हैं, क्योंकि हम यहां आपके मूल प्रश्न का मुद्दा छोड़ रहे हैं।
ब्रेनबर्न

108
यह सभी पायथन रिश्तेदार आयात सवालों का जवाब होना चाहिए। यह डॉक्स में होना चाहिए, यहां तक ​​कि।
edsioufi

10
Python.org/dev/peps/pep-0366 देखें - "ध्यान दें कि यह बॉयलरप्लेट केवल तभी पर्याप्त है जब शीर्ष स्तर का पैकेज पहले से ही sys.path के माध्यम से सुलभ हो। अतिरिक्त कोड जो sys.path में हेरफेर करता है, उसे प्रत्यक्ष निष्पादन के लिए आवश्यक होगा। पहले से आयात किए जा रहे शीर्ष स्तर के पैकेज के बिना काम करना। " - यह मेरे लिए सबसे परेशान करने वाला बिट है क्योंकि यह "अतिरिक्त कोड" वास्तव में काफी लंबा है और आसानी से चलाने के लिए पैकेज में कहीं और संग्रहीत नहीं किया जा सकता है।
माइकल स्कॉट कटहबर्ट

14
इस उत्तर वर्तमान के बारे में कुछ महत्वपूर्ण जानकारी पर बंद है __name__और sys.path। विशेष रूप से, के साथ python -m pkg.mod, के __name__लिए सेट है __main__, नहीं pkg.mod; सापेक्ष आयात इस मामले में उपयोग करने के __package__बजाय हल किए जाते हैं __name__। इसके अलावा, पायथन वर्तमान स्क्रिप्ट के बजाय स्क्रिप्ट की डायरेक्टरी को जोड़ता है sys.pathजब वह चल रहा होता है python path/to/script.py; यह वर्तमान निर्देशिका को जोड़ता है, sys.pathजब अधिकांश अन्य तरीके शामिल हैं, जिसमें शामिल हैं python -m pkg.mod
user2357112

42

यह वास्तव में अजगर के भीतर एक समस्या है। भ्रम की उत्पत्ति यह है कि लोग गलती से सापेक्ष आयात को पथ सापेक्ष के रूप में लेते हैं जो कि नहीं है।

उदाहरण के लिए जब आप faa.py में लिखते हैं :

from .. import foo

इसका केवल एक अर्थ है यदि faa.py को पैकेज के एक भाग के रूप में निष्पादन के दौरान अजगर द्वारा पहचाना और लोड किया गया हो। उस मामले में, मॉड्यूल के नाम के लिए faa.py उदाहरण के लिए होगा some_packagename.faa । यदि फ़ाइल को केवल इसलिए लोड किया गया था क्योंकि यह वर्तमान निर्देशिका में है, जब अजगर चलाया जाता है, तो उसका नाम किसी भी पैकेज को संदर्भित नहीं करेगा और अंततः रिश्तेदार आयात विफल हो जाएगा।

वर्तमान निर्देशिका में मॉड्यूल को संदर्भित करने के लिए एक सरल समाधान, इसका उपयोग करना है:

if __package__ is None or __package__ == '':
    # uses current directory visibility
    import foo
else:
    # uses current package visibility
    from . import foo

5
सही समाधान है from __future__ import absolute_importऔर उपयोगकर्ता को आपके कोड का सही उपयोग करने के लिए मजबूर करें ... ताकि आप हमेशा कर सकेंfrom . import foo
जियाकोमो अल्जेटा

@ गियाकोमो: मेरी समस्या के लिए बिल्कुल सही जवाब। धन्यवाद!
फैबियो

8

यहां एक सामान्य नुस्खा है, उदाहरण के लिए फिट करने के लिए संशोधित, कि मैं अभी पैकेज के रूप में लिखे गए पायथन पुस्तकालयों से निपटने के लिए उपयोग कर रहा हूं, जिसमें अन्योन्याश्रित फाइलें शामिल हैं, जहां मैं उनके टुकड़े के कुछ हिस्सों का परीक्षण करने में सक्षम होना चाहता हूं। चलो इस कहते हैं lib.fooऔर कहते हैं कि यह करने के लिए उपयोग की जरूरत है lib.fileAकार्यों के लिए f1और f2, और lib.fileBवर्ग के लिए Class3

मैंने printयह बताने में मदद के लिए कुछ कॉल शामिल किए हैं कि यह कैसे काम करता है। व्यवहार में आप उन्हें (और शायद from __future__ import print_functionरेखा को भी ) हटाना चाहेंगे ।

यह विशेष उदाहरण दिखाने के लिए बहुत सरल है जब हमें वास्तव में एक प्रविष्टि सम्मिलित करने की आवश्यकता होती है sys.path। ( ऐसे मामले के लिए लार्स का उत्तर देखें जहां हमें इसकी आवश्यकता होती है, जब हमारे पास पैकेज निर्देशिकाओं के दो या दो से अधिक स्तर होते हैं, और फिर हम उपयोग करते हैं os.path.dirname(os.path.dirname(__file__))-लेकिन यह वास्तव में यहां भी चोट नहीं करता है ।) यह बिना ऐसा करने के लिए पर्याप्त सुरक्षित है। if _i in sys.pathपरीक्षा। हालाँकि, यदि प्रत्येक आयातित फ़ाइल एक ही पथ सम्मिलित करती है - उदाहरण के लिए, यदि दोनों fileAऔर fileBपैकेज से उपयोगिताओं को आयात करना चाहते हैं - यह sys.pathएक ही पथ के साथ कई बार टकराता है , तो if _i not in sys.pathबॉयलरप्लेट में होना अच्छा है ।

from __future__ import print_function # only when showing how this works

if __package__:
    print('Package named {!r}; __name__ is {!r}'.format(__package__, __name__))
    from .fileA import f1, f2
    from .fileB import Class3
else:
    print('Not a package; __name__ is {!r}'.format(__name__))
    # these next steps should be used only with care and if needed
    # (remove the sys.path manipulation for simple cases!)
    import os, sys
    _i = os.path.dirname(os.path.abspath(__file__))
    if _i not in sys.path:
        print('inserting {!r} into sys.path'.format(_i))
        sys.path.insert(0, _i)
    else:
        print('{!r} is already in sys.path'.format(_i))
    del _i # clean up global name space

    from fileA import f1, f2
    from fileB import Class3

... all the code as usual ...

if __name__ == '__main__':
    import doctest, sys
    ret = doctest.testmod()
    sys.exit(0 if ret.failed == 0 else 1)

यहाँ विचार यह है (और ध्यान दें कि ये सभी python2.7 और python 3.x पर समान कार्य करते हैं):

  1. यदि सामान्य कोड से नियमित पैकेज आयात के रूप में import libया के from lib import fooरूप में चलाया जाता है , __packageहै libऔर __name__है lib.foo। हम पहले कोड का रास्ता लेते हैं .fileA, आदि से आयात करते हैं ।

  2. यदि के रूप में चलाया जाता है python lib/foo.py, __package__कोई नहीं __name__होगा और होगा __main__

    हम दूसरा कोड पथ लेते हैं। libनिर्देशिका पहले में होगा sys.pathतो इसे जोड़ने के लिए कोई जरूरत नहीं है। हम fileAआदि से आयात करते हैं ।

  3. यदि libनिर्देशिका के भीतर चलाया जाता है python foo.py, तो व्यवहार केस 2 के लिए समान है।

  4. यदि libनिर्देशिका के भीतर चलाया जाता है python -m foo, तो व्यवहार 2 और 3 के मामलों के समान है। हालांकि, libनिर्देशिका का पथ अंदर नहीं है sys.path, इसलिए हम आयात करने से पहले जोड़ते हैं। वही लागू होता है अगर हम पायथन चलाते हैं और फिर import foo

    (जब . से अंदर है sys.path, हमें वास्तव में यहां पथ के पूर्ण संस्करण को जोड़ने की आवश्यकता नहीं है। यह वह जगह है जहां एक गहरा पैकेज घोंसले का निर्माण संरचना है, जहां हम करना चाहते हैं from ..otherlib.fileC import ..., एक फर्क पड़ता है। यदि आप यह नहीं कर रहे हैं, तो आप। sys.pathपूरी तरह से हेरफेर को छोड़ दें ।)

टिप्पणियाँ

अभी भी एक कौवा है। यदि आप इस पूरी चीज़ को बाहर से चलाते हैं:

$ python2 lib.foo

या:

$ python3 lib.foo

व्यवहार की सामग्री पर निर्भर करता है lib/__init__.py। यदि वह मौजूद है और खाली है , तो सब ठीक है:

Package named 'lib'; __name__ is '__main__'

लेकिन अगर lib/__init__.py खुद को आयात करता है routineताकि यह routine.nameसीधे निर्यात कर सके lib.name, तो आप इसे प्राप्त कर सकते हैं:

$ python2 lib.foo
Package named 'lib'; __name__ is 'lib.foo'
Package named 'lib'; __name__ is '__main__'

यही है, मॉड्यूल पैकेज के माध्यम से एक बार, दो बार आयात किया जाता है और फिर फिर से __main__ताकि यह आपके mainकोड को चलाता है । अजगर 3.6 और बाद में इस बारे में चेतावनी देता है:

$ python3 lib.routine
Package named 'lib'; __name__ is 'lib.foo'
[...]/runpy.py:125: RuntimeWarning: 'lib.foo' found in sys.modules
after import of package 'lib', but prior to execution of 'lib.foo';
this may result in unpredictable behaviour
  warn(RuntimeWarning(msg))
Package named 'lib'; __name__ is '__main__'

चेतावनी नया है, लेकिन चेतावनी दी-के बारे में व्यवहार नहीं है। यह इस बात का हिस्सा है कि कुछ लोग डबल इंपोर्ट ट्रैप को कहते हैं । (अतिरिक्त विवरण के लिए समस्या 27487 देखें ।) निक कॉघलान कहते हैं:

यह अगला जाल 3.3 सहित पाइथन के सभी वर्तमान संस्करणों में मौजूद है, और इसे निम्नलिखित सामान्य दिशानिर्देशों में अभिव्यक्त किया जा सकता है: "कभी भी पैकेज निर्देशिका, या पैकेज के अंदर किसी भी निर्देशिका को सीधे पायथन पथ में न जोड़ें"।

ध्यान दें कि जब हम यहां उस नियम का उल्लंघन करते हैं, तो हम इसे केवल तब करते हैं जब लोड की जा रही फ़ाइल को पैकेज के हिस्से के रूप में लोड नहीं किया जा रहा है, और हमारा संशोधन विशेष रूप से हमें उस पैकेज में अन्य फ़ाइलों को एक्सेस करने की अनुमति देने के लिए डिज़ाइन किया गया है। (और, जैसा कि मैंने उल्लेख किया है, हमें संभवतः एकल स्तर के पैकेज के लिए ऐसा नहीं करना चाहिए।) यदि हम अतिरिक्त-स्वच्छ रहना चाहते हैं, तो हम इसे फिर से लिख सकते हैं, जैसे:

    import os, sys
    _i = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    if _i not in sys.path:
        sys.path.insert(0, _i)
    else:
        _i = None

    from sub.fileA import f1, f2
    from sub.fileB import Class3

    if _i:
        sys.path.remove(_i)
    del _i

यही है, हम sys.pathअपने आयातों को प्राप्त करने के लिए लंबे समय तक संशोधित करते हैं , फिर इसे वापस उसी तरह से डालते हैं जैसे ( _iयदि हम केवल एक प्रति जोड़ते हैं , तो उसकी एक प्रति हटाते हैं _i)।


7

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

if __name__ == '__main__':
   # run test code here...

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

# import any site-lib modules first, then...
import sys
parent_module = sys.modules['.'.join(__name__.split('.')[:-1]) or '__main__']
if __name__ == '__main__' or parent_module.__name__ == '__main__':
    from codex import Codex # these are in same folder as module under test!
    from dblogger import DbLogger
else:
    from .codex import Codex
    from .dblogger import DbLogger

यदि विवरण यह देखने के लिए जाँचता है कि क्या हम इस मॉड्यूल को मुख्य के रूप में चला रहे हैं या यदि इसका उपयोग किसी अन्य मॉड्यूल में किया जा रहा है जिसे मुख्य के रूप में परीक्षण किया जा रहा है । शायद यह स्पष्ट है, लेकिन मैं इस नोट को यहां प्रस्तुत करता हूं, यदि कोई अन्य व्यक्ति उपरोक्त आयात मुद्दों से निराश होकर इसका उपयोग कर सकता है।


1
यह वास्तव में इसे हल करता है। लेकिन यह वास्तव में बुरा है। यह डिफ़ॉल्ट व्यवहार क्यों नहीं है ?!
लो tolmencre

4

यहाँ एक समाधान है जिसे मैं सुझा नहीं सकता, लेकिन कुछ स्थितियों में उपयोगी हो सकता है जहां मॉड्यूल बस उत्पन्न नहीं हुए थे:

import os
import sys
parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.append(parent_dir_name + "/your_dir")
import your_script
your_script.a_function()

2

मेरे पास एक ऐसी ही समस्या थी जहां मैं पायथन मॉड्यूल खोज पथ को बदलना नहीं चाहता था और एक स्क्रिप्ट से अपेक्षाकृत मॉड्यूल लोड करने की आवश्यकता थी ( ब्रेनबार को अच्छी तरह से ऊपर बताया गया "स्क्रिप्ट सभी के साथ रिश्तेदार आयात नहीं कर सकता" के बावजूद )।

इसलिए मैंने निम्नलिखित हैक का उपयोग किया। दुर्भाग्य से, यह impमॉड्यूल पर निर्भर करता है जो संस्करण 3.4 के बाद से हटाए जाने के पक्ष में पदावनत हो गया importlib। (क्या यह भी संभव importlibहै? मुझे नहीं पता।) फिर भी, हैक अब के लिए काम करता है।

के सदस्यों तक पहुँचने के लिए उदाहरण moduleXमें subpackage1एक स्क्रिप्ट में रहने वाले से subpackage2फ़ोल्डर:

#!/usr/bin/env python3

import inspect
import imp
import os

def get_script_dir(follow_symlinks=True):
    """
    Return directory of code defining this very function.
    Should work from a module as well as from a script.
    """
    script_path = inspect.getabsfile(get_script_dir)
    if follow_symlinks:
        script_path = os.path.realpath(script_path)
    return os.path.dirname(script_path)

# loading the module (hack, relying on deprecated imp-module)
PARENT_PATH = os.path.dirname(get_script_dir())
(x_file, x_path, x_desc) = imp.find_module('moduleX', [PARENT_PATH+'/'+'subpackage1'])
module_x = imp.load_module('subpackage1.moduleX', x_file, x_path, x_desc)

# importing a function and a value
function = module_x.my_function
VALUE = module_x.MY_CONST

फेडरिको द्वारा बताए गए लोडिंग मॉड्यूल के लिए उपयोग किए जाने वाले sys.path को संशोधित करने के लिए एक क्लीनर दृष्टिकोण लगता है।

#!/usr/bin/env python3

if __name__ == '__main__' and __package__ is None:
    from os import sys, path
    # __file__ should be defined in this case
    PARENT_DIR = path.dirname(path.dirname(path.abspath(__file__)))
   sys.path.append(PARENT_DIR)
from subpackage1.moduleX import *

यह बेहतर लग रहा है ... बहुत बुरा यह अभी भी आप फ़ाइल में मूल निर्देशिका के नाम को एम्बेड करने की आवश्यकता है ... शायद कि importlib के साथ सुधार किया जा सकता है। हो सकता है कि साधारण उपयोग के मामलों के लिए इंपोर्टलीब को रिश्तेदार आयात "सिर्फ काम" करने के लिए भी बंद किया जा सकता है। मैं इस पर एक दरार ले जाऊँगा।
एंड्रयू वागनर

मैं हालांकि 2.7.14 अजगर का उपयोग कर रहा हूँ। क्या ऐसा कुछ अभी भी काम करेगा?
user3474042

मैंने अभी-अभी पायथन 2.7.10 पर दोनों दृष्टिकोणों का परीक्षण किया और उन्होंने मेरे लिए ठीक काम किया। यदि वास्तव में, आपके पास 2.7 में एक छोटा सा छोटा मॉड्यूल की समस्या नहीं है, तो सभी बेहतर हैं।
लार्स

2

__name__ वैश्विक कोड नाम स्थान या आयातित मॉड्यूल के भाग के रूप में प्रश्न में कोड चलाया गया है या नहीं, इसके आधार पर परिवर्तन।

यदि कोड वैश्विक स्थान में नहीं चल रहा है, __name__तो मॉड्यूल का नाम होगा। यदि यह वैश्विक नामस्थान में चल रहा है - उदाहरण के लिए, यदि आप इसे कंसोल में टाइप करते हैं, या मॉड्यूल का उपयोग स्क्रिप्ट के रूप में चलाते हैं python.exe yourscriptnamehere.pyतो __name__बन जाता है "__main__"

आप देखेंगे कि बहुत से अजगर कोड if __name__ == '__main__'का उपयोग यह परीक्षण करने के लिए किया जाता है कि क्या कोड वैश्विक नामस्थान से चलाया जा रहा है - जो आपको एक स्क्रिप्ट के रूप में युगल होने की अनुमति देता है।

क्या आपने कंसोल से इन आयातों को करने की कोशिश की?


आह, तो आप उल्लेख करते हैं -एम। यह आपके मॉड्यूल को एक स्क्रिप्ट के रूप में निष्पादित करता है - यदि आप एक __name__ == '__main__' चिपकाते हैं तो आपको यह देखना चाहिए कि यह -m की वजह से '__main__' है। बस अपने मॉड्यूल को दूसरे मॉड्यूल में आयात करने की कोशिश करें ताकि यह शीर्ष स्तर न हो ... जो आपको सापेक्ष आयात करने की अनुमति देनी चाहिए
Theodox

मैंने कंसोल से इन आयातों को करने की कोशिश की, जिसमें सक्रिय फ़ाइल सही मॉड्यूल है।

@Stopforgettingmyaccounts ...: क्या मतलब है "सक्रिय फ़ाइल"?
ब्रेनबार

मैं Pyscripter का उपयोग करें। जब मैं इन आयातों को चला रहा था, तो मैं मॉड्यूलएक्सएफ़डीओएम में था: .ModuleY आयात स्पैम और से। आयात मॉड्यूल।

इंपोर्ट नहीं .moduleY इसके बाद मॉड्यूलY.spam ()?
थियोडोक्स

2

@ ब्रेनबर्न का जवाब यह सब कहता है, लेकिन अगर आप मेरे जैसे हैं तो समझने में थोड़ा समय लग सकता है। यहाँ मेरा मामला है और कैसे @ ब्रेनबर्न का उत्तर इस पर लागू होता है, शायद यह आपकी मदद करेगा।

मुकदमा

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
    moduleA.py

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

समाधान

पैकेज और आयात पैकेज के समान स्तर पर परीक्षण स्क्रिप्ट को स्थानांतरित करें। subpackage1.moduleX

व्याख्या

जैसा कि समझाया गया है, वर्तमान नाम के सापेक्ष सापेक्ष आयात किया जाता है। जब मेरी टेस्ट स्क्रिप्ट उसी निर्देशिका से मॉड्यूलएक्स आयात करती है, तो मॉड्यूलएक्स के अंदर मॉड्यूल का नाम मॉड्यूलएक्स होता है। जब यह एक रिश्तेदार आयात का सामना करता है तो दुभाषिया पैकेज पदानुक्रम को वापस नहीं कर सकता क्योंकि यह पहले से ही शीर्ष पर है

जब मैं ऊपर से मॉड्यूलएक्स आयात करता हूं, तो मॉड्यूलएक्स के अंदर नाम पैकेज होता है। एसक्यूपैकजेज 1.मोडुएलएक्स और रिश्तेदार आयात पाया जा सकता है


आशा है कि आप इस पर मेरा मार्गदर्शन कर सकते हैं। निम्नलिखित लिंक में, यदि आप केस 3 पर जाते हैं, तो यह कहता है कि समाधान 1 संभव नहीं है। कृपया आप इसकी जाँच कर सकते हैं और मुझे बता सकते हैं। यह मुझे बहुत मदद करेगा। chrisyeh96.github.io/2017/08/08/…
चर

@ मुख्य लिंक में एक टाइपो है और मुझे संपादित करने की अनुमति नहीं है। केस 3 को देखा और ठीक वैसा ही अनुसरण नहीं किया जैसा आप देख रहे हैं। जब मैंने अजगर 2 में उस उदाहरण की कोशिश की तो कोई समस्या नहीं थी जो मुझे लगता है कि मुझे कुछ याद आया। हो सकता है कि आपको एक नया प्रश्न पोस्ट करना चाहिए लेकिन एक स्पष्ट उदाहरण प्रदान करने की आवश्यकता है। केस 4 यहाँ मेरे जवाब में मैं जो बात कर रहा हूँ, उस पर छूता है: आप किसी रिश्तेदार के आयात के लिए निर्देशिका नहीं बना सकते हैं। दुभाषिया एक मूल निर्देशिका में शुरू होता है
ब्रैड ड्रे

धन्यवाद मैं अजगर 3 का उल्लेख कर रहा हूँ और यहाँ सवाल stackoverflow.com/questions/58577767/…
चर

1

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

PyPi को थोड़ा अजगर पैकेज लिखा जो इस प्रश्न के दर्शकों की मदद कर सकता है। पैकेज वर्कअराउंड के रूप में कार्य करता है, यदि कोई अजगर फ़ाइल को आयात करने में सक्षम होना चाहिए, जिसमें ऊपरी स्तर के पैकेज वाले आयात शामिल हैं जो कि पैकेज / प्रोजेक्ट में सीधे आयात करने वाले फ़ाइल की निर्देशिका में नहीं है। https://pypi.org/project/import-anywhere/


-2

पाइथन को मेरे पास वापस न करने के लिए "नॉन-पैकेज में रिश्तेदार के आयात का प्रयास"। पैकेज /

init .py subpackage1 / init .py moduleX.py moduleY.py subpackage2 / init .py moduleZ.py moduleA.py

यह त्रुटि तब होती है जब आप मूल फ़ाइल के सापेक्ष आयात लागू कर रहे हैं। उदाहरण के लिए पैरेंट फ़ाइल पहले ही मुख्य वापस आ जाती है क्योंकि आप मॉड्यूलए-थिसिस में "प्रिंट ( नाम )" कोड के बाद । थिस फ़ाइल पहले से ही मुख्य हैयह आगे किसी भी मूल पैकेज को वापस नहीं कर सकता है। संकुल उपखंड 1 और सबपैकेज 2 की फाइलों में सापेक्षिक आयात की आवश्यकता होती है जिसका उपयोग आप ".." के लिए कर सकते हैं "।" ऐसी फाइलें जहां आप माता-पिता को रिश्तेदार आयात कर रहे हैं, केवल पूर्ण आयात के आवेदन के साथ काम कर सकते हैं। अगर आप PARENT पैकेज में ABSOLUTE आयात का उपयोग करेंगे तो कोई भी ERROR नहीं आएगा क्योंकि अजगर जानता है कि पैकेज के शीर्ष स्तर पर कौन है, भले ही आपकी फ़ाइल PYTHON PATH की अवधारणा के कारण उप-पैकेज में हो, परियोजना के शीर्ष स्तर को परिभाषित करता है

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.