ব্যাখ্যা
পিইপি 328 থেকে
আপেক্ষিক আমদানিগুলি প্যাকেজ শ্রেণিবিন্যাসের ক্ষেত্রে মডিউলটির অবস্থান নির্ধারণ করতে মডিউলের __name__ বৈশিষ্ট্য ব্যবহার করে। যদি মডিউলের নামটিতে কোনও প্যাকেজ সম্পর্কিত তথ্য না থাকে (যেমন এটি '__main__' সেট করা থাকে) তবে আপেক্ষিক আমদানিগুলি সমাধান করা হয় যেন মডিউলটি একটি শীর্ষ স্তরের মডিউল , ফাইল মেশিনটি
প্রকৃতপক্ষে ফাইল সিস্টেমে অবস্থিত কিনা তা নির্বিশেষে।
কিছু সময় পিইপি 338 পিইপি 328 এর সাথে সাংঘর্ষিক :
... প্যাকেজ শ্রেণিবিন্যাসের বর্তমান মডিউলটির অবস্থান নির্ধারণ করতে আপেক্ষিক আমদানিগুলি __ নাম__ এর উপর নির্ভর করে একটি প্রধান মডিউলে, __name__ এর মান সর্বদা '__main__' থাকে , সুতরাং সুস্পষ্ট আপেক্ষিক আমদানি সর্বদা ব্যর্থ হবে (কারণ তারা কেবলমাত্র একটি প্যাকেজের অভ্যন্তরে মডিউলের জন্য কাজ করে)
এবং সমস্যাটি সমাধান করার জন্য, পিইপি 366 শীর্ষ স্তরের ভেরিয়েবল প্রবর্তন করেছে __package__
:
একটি নতুন মডিউল স্তরের বৈশিষ্ট্য যুক্ত করে, এই পিইপি -m
সুইচ ব্যবহার করে মডিউলটি কার্যকর করা হলে আপেক্ষিক আমদানিগুলি স্বয়ংক্রিয়ভাবে কাজ করার অনুমতি দেয় । মডিউলটিতে অল্প পরিমাণে বয়লারপ্লেট নিজেই যখন ফাইলটি নাম দ্বারা সম্পাদন করা হয় তখন আপেক্ষিক আমদানিগুলি কাজ করতে দেয়। [...] যখন এটি [বৈশিষ্ট্য] উপস্থিত থাকে, আপেক্ষিক আমদানিগুলি মডিউল __name__ বৈশিষ্ট্যের পরিবর্তে এই বৈশিষ্ট্যের উপর ভিত্তি করে তৈরি করা হবে । [...] যখন প্রধান মডিউলটি তার ফাইল নাম দ্বারা নির্দিষ্ট করা হয়, তারপরে __package__ গুণটি কোনওটির জন্য সেট করা হবে না । [...] যখন আমদানি সিস্টেমটি __package__ সেট ছাড়াই মডিউলে একটি সুস্পষ্ট আপেক্ষিক আমদানির মুখোমুখি হয় (বা এটির সাথে সেট করা থাকে না), এটি সঠিক মান গণনা করে এবং সংরক্ষণ করে (সাধারণ মডিউলগুলির জন্য __name __। RPPitionition ('।') [0] এবং প্যাকেজ প্রারম্ভিককরণ মডিউলগুলির জন্য __ নাম__ )
(জোর আমার)
যদি __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;
}
সিপাইথন এই ব্যতিক্রমটি উত্থাপন করে যদি এটি 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
তবে আপনি এটি কেবল পাইথন ৩.6 বা আরও নতুনতে দেখতে পাবেন।
সমাধান # 1: -ম ব্যবহার করে আপনার স্ক্রিপ্টটি চালান
একটি ডিরেক্টরি বিবেচনা করুন (যা পাইথন প্যাকেজ ):
.
├── package
│ ├── __init__.py
│ ├── module.py
│ └── standalone.py
প্যাকেজের সমস্ত ফাইলই কোডের একই 2 লাইন দিয়ে শুরু হয়:
from pathlib import Path
print('Running' if __name__ == '__main__' else 'Importing', Path(__file__).resolve())
আমি কেবল দুটি অপারেশন ক্রম সুস্পষ্ট করতে এই দুটি লাইন অন্তর্ভুক্ত করছি including আমরা এগুলিকে সম্পূর্ণ উপেক্ষা করতে পারি, যেহেতু তারা মৃত্যুদন্ড কার্যকর করে না।
__init__.py এবং Module.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__ সেট করুন
দয়া করে এটি প্রকৃত সমাধানের চেয়ে ধারণার প্রমাণ হিসাবে বিবেচনা করুন। এটি রিয়েল-ওয়ার্ল্ড কোডে ব্যবহারের জন্য উপযুক্ত নয়।
পিইপি 366 এর এই সমস্যার একদম কাজ রয়েছে, তবে এটি অসম্পূর্ণ, কারণ __package__
একা সেট করা যথেষ্ট নয়। আপনাকে মডিউল হায়ারার্কিতে কমপক্ষে N পূর্ববর্তী প্যাকেজগুলি আমদানি করতে হবে , যেখানে এন হ'ল প্যারেন্ট ডিরেক্টরিগুলির সংখ্যা (স্ক্রিপ্টের ডিরেক্টরিটির সাথে সম্পর্কিত) যা মডিউলটি আমদানি করার জন্য অনুসন্ধান করা হবে।
সুতরাং,
বর্তমান মডিউলটির 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
এটি আমাদের ফাইলের নাম দিয়ে standalone.py সম্পাদন করতে দেয় :
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
যেখানে 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
standalone.py সমস্যা ছাড়াই চলে:
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'>
আমার মনে হয় আমার আপনাকে সতর্ক করা উচিত: এটি করার চেষ্টা করবেন না, বিশেষত যদি আপনার প্রকল্পের কাঠামো জটিল থাকে।
পার্শ্ব দ্রষ্টব্য হিসাবে, পিইপি 8 নিখুঁত আমদানি ব্যবহারের পরামর্শ দিচ্ছে, তবে কয়েকটি পরিস্থিতিতে স্পষ্টত আপেক্ষিক আমদানি গ্রহণযোগ্য:
নিখুঁত আমদানি সুপারিশ করা হয়, কারণ এগুলি সাধারণত বেশি পাঠযোগ্য এবং আরও ভাল আচরণ করার ঝোঁক রয়েছে (বা কমপক্ষে আরও ভাল ত্রুটির বার্তা দিন)। [...] তবে, সুস্পষ্ট আপেক্ষিক আমদানি নিখুঁত আমদানির একটি গ্রহণযোগ্য বিকল্প, বিশেষত যখন জটিল প্যাকেজ লেআউটগুলি নিয়ে কাজ করে যেখানে নিরঙ্কুশ আমদানি ব্যবহার করা অযথা ভার্চুজের হয়ে থাকে।