व्याख्या
से पीईपी 328
पैकेज पदानुक्रम में उस मॉड्यूल की स्थिति निर्धारित करने के लिए सापेक्ष आयात एक मॉड्यूल की __name__ विशेषता का उपयोग करते हैं। यदि मॉड्यूल के नाम में कोई पैकेज जानकारी नहीं है (उदाहरण के लिए यह '__main__' पर सेट है)
तो सापेक्ष आयात हल किए जाते हैं जैसे कि मॉड्यूल एक शीर्ष स्तर मॉड्यूल था , चाहे मॉड्यूल वास्तव में फ़ाइल सिस्टम पर स्थित हो।
कुछ बिंदु पर PEP 338 PEP 328 के साथ विवादित है :
... सापेक्ष आयात पैकेज पदानुक्रम में वर्तमान मॉड्यूल की स्थिति निर्धारित करने के लिए __name__ पर निर्भर करता है । एक मुख्य मॉड्यूल में, __name__ का मान हमेशा '__main__' होता है , इसलिए स्पष्ट सापेक्ष आयात हमेशा विफल होंगे (क्योंकि वे केवल पैकेज के अंदर एक मॉड्यूल के लिए काम करते हैं)
और समस्या को हल करने के लिए, PEP 366 ने शीर्ष स्तर चर पेश किया __package__
:
एक नया मॉड्यूल स्तर विशेषता जोड़कर, यह पीईपी स्वचालित रूप से काम करने की अनुमति देता है यदि मॉड्यूल -m
स्विच का उपयोग करके निष्पादित किया जाता है । मॉड्यूल में बॉयलरप्लेट की थोड़ी मात्रा स्वयं ही रिश्तेदार आयात को काम करने की अनुमति देगा जब फ़ाइल को नाम से निष्पादित किया जाता है। [...] जब यह [विशेषता] मौजूद है, रिश्तेदार आयात मॉड्यूल __name__ विशेषता के बजाय इस विशेषता पर आधारित होंगे । [...] जब मुख्य मॉड्यूल इसके फ़ाइलनाम द्वारा निर्दिष्ट किया जाता है, तो __package__ विशेषता को किसी पर सेट नहीं किया जाएगा । [...] जब आयात प्रणाली __package__ सेट के बिना एक मॉड्यूल में एक स्पष्ट सापेक्ष आयात का सामना करती है (या इसके साथ कोई भी सेट नहीं करता है), तो यह सही मूल्य की गणना करेगा और संग्रहीत करेगा ()सामान्य मॉड्यूल के लिए __name __। rpartition ('।') [0] और पैकेज आरंभीकरण मॉड्यूल के लिए __name__ )
(जोर मेरा)
यदि __name__
है '__main__'
, तो __name__.rpartition('.')[0]
खाली स्ट्रिंग लौटाता है। यही कारण है कि त्रुटि विवरण में खाली स्ट्रिंग शाब्दिक है:
SystemError: Parent module '' not loaded, cannot perform relative import
सीपीथॉन के PyImport_ImportModuleLevelObject
समारोह का प्रासंगिक हिस्सा :
if (PyDict_GetItem(interp->modules, package) == NULL) {
PyErr_Format(PyExc_SystemError,
"Parent module %R not loaded, cannot perform relative "
"import", package);
goto error;
}
CPython इस अपवाद को उठाता है अगर यह package
(पैकेज का नाम) interp->modules
(जैसा कि सुलभ sys.modules
) में नहीं मिल रहा था। के बाद से sys.modules
है "एक शब्दकोश मॉड्यूल जो पहले से ही लोड हो जाने के लिए मॉड्यूल के नाम के नक्शे है कि" , यह अब स्पष्ट है कि माता-पिता मॉड्यूल रिश्तेदार आयात प्रदर्शन से पहले स्पष्ट रूप से पूर्ण आयातित होना चाहिए ।
नोट: 18018 के अंक के पैचने एक और if
ब्लॉक जोड़ा है, जिसेऊपर दिए गए कोड से पहले निष्पादित किया जाएगा:
if (PyUnicode_CompareWithASCIIString(package, "") == 0) {
PyErr_SetString(PyExc_ImportError,
"attempted relative import with no known parent package");
goto error;
} /* else if (PyDict_GetItem(interp->modules, package) == NULL) {
...
*/
यदि package
(ऊपर जैसा है) खाली स्ट्रिंग है, तो त्रुटि संदेश होगा
ImportError: attempted relative import with no known parent package
हालाँकि, आप इसे केवल पायथन 3.6 या नए में देखेंगे।
समाधान # 1: -m का उपयोग करके अपनी स्क्रिप्ट चलाएँ
एक निर्देशिका पर विचार करें (जो एक पायथन पैकेज है ):
.
├── package
│ ├── __init__.py
│ ├── module.py
│ └── standalone.py
पैकेज की सभी फाइलें कोड की समान 2 पंक्तियों से शुरू होती हैं:
from pathlib import Path
print('Running' if __name__ == '__main__' else 'Importing', Path(__file__).resolve())
मैं केवल परिचालन के क्रम को स्पष्ट करने के लिए इन दो पंक्तियों को शामिल कर रहा हूँ । हम उन्हें पूरी तरह से अनदेखा कर सकते हैं, क्योंकि वे निष्पादन को प्रभावित नहीं करते हैं।
__init__.py और मॉड्यूल.py में केवल वे दो पंक्तियाँ होंगी (यानी, वे प्रभावी रूप से खाली हैं)।
standalone.py अतिरिक्त मॉड्यूल आयात करने का प्रयास करता है सापेक्ष आयात के माध्यम से:
from . import module # explicit relative import
हम अच्छी तरह जानते हैं कि /path/to/python/interpreter package/standalone.py
असफल होंगे। हालांकि, हम साथ मॉड्यूल चला सकते हैं -m
आदेश पंक्ति विकल्प कि "खोज sys.path
नामित मॉड्यूल के लिए और के रूप में उसकी सामग्री पर अमल __main__
मॉड्यूल" :
vaultah@base:~$ python3 -i -m package.standalone
Importing /home/vaultah/package/__init__.py
Running /home/vaultah/package/standalone.py
Importing /home/vaultah/package/module.py
>>> __file__
'/home/vaultah/package/standalone.py'
>>> __package__
'package'
>>> # The __package__ has been correctly set and module.py has been imported.
... # What's inside sys.modules?
... import sys
>>> sys.modules['__main__']
<module 'package.standalone' from '/home/vaultah/package/standalone.py'>
>>> sys.modules['package.module']
<module 'package.module' from '/home/vaultah/package/module.py'>
>>> sys.modules['package']
<module 'package' from '/home/vaultah/package/__init__.py'>
-m
क्या आपके लिए सभी आयात करने वाला सामान और स्वचालित रूप से सेट हो जाता है __package__
, लेकिन आप स्वयं ऐसा कर सकते हैं
समाधान # 2: सेट __package__ मैन्युअल रूप से
कृपया इसे वास्तविक समाधान के बजाय अवधारणा के प्रमाण के रूप में मानें। यह वास्तविक दुनिया कोड में उपयोग के लिए अच्छी तरह से अनुकूल नहीं है।
PEP 366 में इस समस्या का समाधान है, हालांकि, यह अधूरा है, क्योंकि __package__
अकेले सेट करना पर्याप्त नहीं है। आपको मॉड्यूल पदानुक्रम में कम से कम एन पूर्ववर्ती पैकेज आयात करने की आवश्यकता है , जहां एन मूल निर्देशिका (स्क्रिप्ट की निर्देशिका के सापेक्ष) की संख्या है जिसे आयात किए जा रहे मॉड्यूल के लिए खोजा जाएगा।
इस प्रकार,
वर्तमान मॉड्यूल के Nth पूर्ववर्ती की मूल निर्देशिका को इसमें जोड़ेंsys.path
वर्तमान फ़ाइल की निर्देशिका को निकालें sys.path
पूरी तरह से योग्य नाम का उपयोग करके वर्तमान मॉड्यूल के मूल मॉड्यूल को आयात करें
2__package__
से पूरी तरह से योग्य नाम पर सेट करें
रिश्तेदार आयात करते हैं
मैं समाधान # 1 से फ़ाइलें उधार लूंगा और कुछ और उप-पैकेज जोड़ूंगा:
package
├── __init__.py
├── module.py
└── subpackage
├── __init__.py
└── subsubpackage
├── __init__.py
└── standalone.py
इस बार स्टैंडअलोनहोम निम्नलिखित सापेक्ष आयात का उपयोग करके पैकेज से मॉड्यूल थिंकपैड आयात करेगा
from ... import module # N = 3
हमें बॉयलरलाइन कोड के साथ उस लाइन को पूर्ववर्ती करने की आवश्यकता होगी, जिससे यह काम कर सके।
import sys
from pathlib import Path
if __name__ == '__main__' and __package__ is None:
file = Path(__file__).resolve()
parent, top = file.parent, file.parents[3]
sys.path.append(str(top))
try:
sys.path.remove(str(parent))
except ValueError: # Already removed
pass
import package.subpackage.subsubpackage
__package__ = 'package.subpackage.subsubpackage'
from ... import module # N = 3
यह हमें फ़ाइल नाम से स्टैंडअलोनहोम निष्पादित करने की अनुमति देता है :
vaultah@base:~$ python3 package/subpackage/subsubpackage/standalone.py
Running /home/vaultah/package/subpackage/subsubpackage/standalone.py
Importing /home/vaultah/package/__init__.py
Importing /home/vaultah/package/subpackage/__init__.py
Importing /home/vaultah/package/subpackage/subsubpackage/__init__.py
Importing /home/vaultah/package/module.py
एक समारोह में लिपटे एक अधिक सामान्य समाधान यहां पाया जा सकता है । उदाहरण उपयोग:
if __name__ == '__main__' and __package__ is None:
import_parents(level=3) # N = 3
from ... import module
from ...module.submodule import thing
समाधान # 3: पूर्ण आयात और सेटपूल का उपयोग करें
कदम हैं -
समान पूर्ण आयात के साथ स्पष्ट सापेक्ष आयात बदलें
package
इसे आयात करने योग्य बनाने के लिए स्थापित करें
उदाहरण के लिए, निर्देशिका संरचना निम्नानुसार हो सकती है
.
├── project
│ ├── package
│ │ ├── __init__.py
│ │ ├── module.py
│ │ └── standalone.py
│ └── setup.py
जहां सेटअप है
from setuptools import setup, find_packages
setup(
name = 'your_package_name',
packages = find_packages(),
)
बाकी फाइलें समाधान # 1 से उधार ली गई थीं ।
स्थापना आपको अपनी कार्यशील निर्देशिका की परवाह किए बिना पैकेज को आयात करने की अनुमति देगा (यह मानते हुए कि कोई नामकरण समस्या नहीं होगी)।
हम इस लाभ (चरण 1) का उपयोग करने के लिए स्टैंडअलोनहोम को संशोधित कर सकते हैं :
from package import module # absolute import
अपनी कार्यशील निर्देशिका को बदलें project
और चलाएं /path/to/python/interpreter setup.py install --user
( अपनी साइट-संकुल निर्देशिका--user
में पैकेज स्थापित करता है ) (चरण 2):
vaultah@base:~$ cd project
vaultah@base:~/project$ python3 setup.py install --user
आइए सत्यापित करें कि स्क्रिप्ट के रूप में स्टैंडअलोनहोम चलाना अब संभव है :
vaultah@base:~/project$ python3 -i package/standalone.py
Running /home/vaultah/project/package/standalone.py
Importing /home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/__init__.py
Importing /home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/module.py
>>> module
<module 'package.module' from '/home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/module.py'>
>>> import sys
>>> sys.modules['package']
<module 'package' from '/home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/__init__.py'>
>>> sys.modules['package.module']
<module 'package.module' from '/home/vaultah/.local/lib/python3.6/site-packages/your_package_name-0.0.0-py3.6.egg/package/module.py'>
नोट : यदि आप इस मार्ग से नीचे जाने का निर्णय लेते हैं, तोअलगाव में पैकेजों को स्थापित करने के लिए आभासी वातावरण का उपयोग करना बेहतर होगा।
समाधान # 4: पूर्ण आयात और कुछ बॉयलरप्लेट कोड का उपयोग करें
सच कहूँ तो, इंस्टॉलेशन आवश्यक नहीं है - पूर्ण आयात कार्य करने के लिए आप अपनी स्क्रिप्ट में कुछ बॉयलरप्लेट कोड जोड़ सकते हैं।
मैं समाधान # 1 से फ़ाइलों को उधार लेने जा रहा हूं और स्टैंडअलोन एरोमा को बदलूंगा :
पूर्ण आयात का उपयोग करके पैकेज से कुछ भी आयात करने का प्रयास करने से पहले पैकेज की मूल निर्देशिका को जोड़ें :sys.path
import sys
from pathlib import Path # if you haven't already done so
file = Path(__file__).resolve()
parent, root = file.parent, file.parents[1]
sys.path.append(str(root))
# Additionally remove the current file's directory from sys.path
try:
sys.path.remove(str(parent))
except ValueError: # Already removed
pass
पूर्ण आयात द्वारा सापेक्ष आयात को बदलें:
from package import module # absolute import
स्टैंडअलोनहोम समस्याओं के बिना चलता है:
vaultah@base:~$ python3 -i package/standalone.py
Running /home/vaultah/package/standalone.py
Importing /home/vaultah/package/__init__.py
Importing /home/vaultah/package/module.py
>>> module
<module 'package.module' from '/home/vaultah/package/module.py'>
>>> import sys
>>> sys.modules['package']
<module 'package' from '/home/vaultah/package/__init__.py'>
>>> sys.modules['package.module']
<module 'package.module' from '/home/vaultah/package/module.py'>
मुझे लगता है कि मुझे आपको चेतावनी देनी चाहिए: ऐसा करने की कोशिश न करें, खासकर अगर आपकी परियोजना में एक जटिल संरचना है।
एक साइड नोट के रूप में, PEP 8 पूर्ण आयात के उपयोग की सिफारिश करता है, लेकिन बताता है कि कुछ परिदृश्यों में स्पष्ट सापेक्ष आयात स्वीकार्य हैं:
पूर्ण आयात की सिफारिश की जाती है, क्योंकि वे आमतौर पर अधिक पठनीय होते हैं और बेहतर व्यवहार करने की प्रवृत्ति होती है (या कम से कम बेहतर त्रुटि संदेश देते हैं)। [...] हालांकि, स्पष्ट सापेक्ष आयात पूर्ण आयात के लिए एक स्वीकार्य विकल्प है, खासकर जब जटिल पैकेज लेआउट के साथ काम करते हुए जहां पूर्ण आयात का उपयोग अनावश्यक रूप से किया जाएगा।