পাইথন 3 এ আপেক্ষিক আমদানি


711

আমি একই ডিরেক্টরিতে অন্য একটি ফাইল থেকে একটি ফাংশন আমদানি করতে চাই।

কখনও কখনও এটি আমার সাথে কাজ করে from .mymodule import myfunctionতবে কখনও কখনও আমি এটি পাই:

SystemError: Parent module '' not loaded, cannot perform relative import

কখনও কখনও এটি সাথে কাজ করে from mymodule import myfunction, তবে কখনও কখনও আমি এটিও পাই:

SystemError: Parent module '' not loaded, cannot perform relative import

আমি এখানে যুক্তি বুঝতে পারি না, এবং আমি কোনও ব্যাখ্যা খুঁজে পাইনি। এটি সম্পূর্ণ এলোমেলো দেখায়।

কেউ আমাকে বোঝাতে পারেন যে এই সমস্ত কিছুর পিছনে যুক্তি কি?


76
এর অর্থ আপনি স্ক্রিপ্ট হিসাবে প্যাকেজের ভিতরে একটি মডিউল চালাচ্ছেন। প্যাকেজের বাইরে থেকে কেবল স্ক্রিপ্টগুলি চালান ।
মার্টিজান পিটারস

3
সম্ভবত আপনি যে শর্তগুলি উল্লেখ করেছেন সেগুলি আপনার নির্দিষ্ট করা উচিত sometimes আমি বুঝতে পেরেছি এর অর্থ এই নয় যে আপনার এলোমেলো ত্রুটি রয়েছে।
জোয়াকুইন

15
@ মার্তিজজনপিটারস: ভাল, দুর্ভাগ্যক্রমে, এই মডিউলটি প্যাকেজের ভিতরে থাকা দরকার, এবং এটি কখনও কখনও স্ক্রিপ্ট হিসাবে চালানোও দরকার। কোন ধারণা আমি কীভাবে এটি অর্জন করতে পারি?
জন স্মিথ

22
@ জনস্মিথ অপশনাল: প্যাকেজগুলির অভ্যন্তরে স্ক্রিপ্টগুলি মেশানো জটিল এবং যদি সম্ভব হয় তবে এড়ানো উচিত। একটি মোড়ক স্ক্রিপ্ট ব্যবহার করুন যা প্যাকেজ আমদানি করে এবং পরিবর্তে আপনার 'স্ক্রিপি' ফাংশনটি চালায়।
মার্টিজন পিটারস

3
দুর্ভাগ্যজনক মনে হচ্ছে। আমি ক্লাস / পদ্ধতিগুলি দিয়ে একটি মূল মডিউল তৈরি করেছি যা একটি নির্দিষ্ট ধরণের ফাইলকে বিশ্লেষণ / বিশ্লেষণ করতে পারে এবং আমার কাছে (মূলত আমার নিজের জন্য) পৃথক মাধ্যমিক মডিউল এবং স্ক্রিপ্ট রয়েছে যা এগুলি আমদানি করে - এই ফাইলগুলি ম্যাসেজ / রূপান্তর করতে পারে। তবে আমি সেই একক কোর ফাইলটি (একটি সম্পূর্ণ জটিল প্যাকেজ নয়) শেষ ব্যবহারকারীর কাছে হস্ত করতে সক্ষম হতে চাই যাতে তারা সহজেই এটিকে তাদের ফাইলের পাশে স্থাপন করতে এবং চালাতে পারে। "স্ক্রিপ্ট মোড" এ এটি ফাইল এবং এনকোডিংকে বিশ্লেষণ করে বিশ্লেষণ করে বিভিন্ন ক্ষেত্র / মান / বিশেষ অক্ষরকে দীর্ঘায়িত করে এবং একটি প্রতিবেদন দেয়। তবে এটি আসলে ফাইলটি পরিবর্তন করে না। এন্টি-প্যাটার্ন?
জন কুম্বস

উত্তর:


526

দুর্ভাগ্যক্রমে, এই মডিউলটি প্যাকেজের অভ্যন্তরে থাকা দরকার এবং এটি কখনও কখনও স্ক্রিপ্ট হিসাবে চালানোও দরকার। কোন ধারণা আমি কীভাবে এটি অর্জন করতে পারি?

এটির মতো লেআউটটি পাওয়া খুব সাধারণ ...

main.py
mypackage/
    __init__.py
    mymodule.py
    myothermodule.py

... mymodule.pyএরকম একটি দিয়ে ...

#!/usr/bin/env python3

# Exported function
def as_int(a):
    return int(a)

# Test function for module  
def _test():
    assert as_int('1') == 1

if __name__ == '__main__':
    _test()

... myothermodule.pyএরকম ...

#!/usr/bin/env python3

from .mymodule import as_int

# Exported function
def add(a, b):
    return as_int(a) + as_int(b)

# Test function for module  
def _test():
    assert add('1', '1') == 2

if __name__ == '__main__':
    _test()

... এবং main.pyএই মত ...

#!/usr/bin/env python3

from mypackage.myothermodule import add

def main():
    print(add('1', '1'))

if __name__ == '__main__':
    main()

... যা আপেক্ষিক আমদানির কারণে আপনি দৌড়ানোর সময় main.pyবা mypackage/mymodule.pyঠিকঠাক কাজ করেন mypackage/myothermodule.py, কিন্তু এতে ব্যর্থ হন ...

from .mymodule import as_int

আপনি যেভাবে এটি চালানোর কথা ...

python3 -m mypackage.myothermodule

... তবে এটি কিছুটা ভার্বোজ, এবং শেবাং লাইনের মতো ভালভাবে মিশে না #!/usr/bin/env python3

নামটি mymoduleবিশ্বব্যাপী অনন্য বলে ধরে নিলে এই ক্ষেত্রে সবচেয়ে সহজ সমাধান হ'ল আপেক্ষিক আমদানিগুলি এড়ানো এবং কেবল ব্যবহার করা ...

from mymodule import as_int

... যদিও এটি অনন্য না হলে বা আপনার প্যাকেজ কাঠামো আরও জটিল, আপনাকে আপনার প্যাকেজ ডিরেক্টরিটি অন্তর্ভুক্ত ডিরেক্টরিটি অন্তর্ভুক্ত PYTHONPATHকরতে হবে এবং এটি এর মতো করুন ...

from mypackage.mymodule import as_int

... বা আপনি যদি এটি "বাক্সের বাইরে" কাজ করতে চান তবে আপনি PYTHONPATHপ্রথমে কোডটি ফ্রেব করতে পারেন এটি দিয়ে ...

import sys
import os

PACKAGE_PARENT = '..'
SCRIPT_DIR = os.path.dirname(os.path.realpath(os.path.join(os.getcwd(), os.path.expanduser(__file__))))
sys.path.append(os.path.normpath(os.path.join(SCRIPT_DIR, PACKAGE_PARENT)))

from mypackage.mymodule import as_int

এটি এক ধরনের বেদনা, তবে নির্দিষ্ট গুইডো ভ্যান রসমের লিখিত ইমেলটিতে কেন একটি চিহ্ন রয়েছে ...

আমি এটির জন্য এবং __main__ যন্ত্রপাতিটির কোনও প্রস্তাবিত বার্ধক্যে -1 আছি । একমাত্র ব্যবহারের ক্ষেত্রে স্ক্রিপ্টগুলি চলমান বলে মনে হচ্ছে যেগুলি মডিউলটির ডিরেক্টরিতে বাস করে যা আমি সবসময় একটি অ্যান্টিপ্যাটার্ন হিসাবে দেখেছি। আমাকে আমার মন পরিবর্তন করতে আপনাকে আমাকে বোঝাতে হবে যে এটি তা নয়।

কোনও প্যাকেজের ভিতরে স্ক্রিপ্টগুলি চালিয়ে যাওয়া কোনও অ্যান্টিপ্যাটার্ন কিনা তা বিষয়ভিত্তিক নয়, তবে ব্যক্তিগতভাবে আমি আমার প্যাকেজের মধ্যে এটি বেশ কার্যকর বলে মনে করি যার মধ্যে কিছু কাস্টম ডাব্লুপিথন উইজেট রয়েছে, সুতরাং আমি wx.Frameকেবলমাত্র একটি উপস্থিত ফাইল প্রদর্শন করার জন্য উত্স ফাইলগুলির জন্য স্ক্রিপ্টটি চালাতে পারি পরীক্ষার উদ্দেশ্যে যে উইজেট।


7
স্ক্রিপ্টটিআইআর করার একটি আরও ভাল উপায় একটি আপেক্ষিক পথ থেকে একটি মডিউল আমদানির মন্তব্যে দেওয়া হয়েছে যেমন os.path.realpath(os.path.dirname(inspect.getfile(inspect.currentframe())))আপনার আত্মবিশ্বাস যে আপনার মডিউলটি সর্বদা যথাযথ fileআপনি ব্যবহার করতে পারেন os.path.realpath(os.path.dirname(__file__))
মার্জ

2
আরও সংক্ষিপ্ত এবং পঠনযোগ্য কোড স্নিপেট প্রয়োগ করে আপনি আপনার পাইথনপথটি প্রসারিত করতে পারেন: sys.path.append( os.path.join( os.path.dirname(__file__), os.path.pardir ) )
অ্যালেক্স-বোগদানভ

12
...which I've always seen as an antipattern.আমি দেখতে পাচ্ছি না এটি কীভাবে একটি বিরোধী প্যাটার্ন ... মনে হচ্ছে আপেক্ষিক আমদানি স্বজ্ঞাতভাবে কাজ করা ভাল সুবিধাজনক হবে। আমি কেবল যে জিনিসগুলি জানি সেগুলি একই ডিরেক্টরিতে আমদানি করতে সক্ষম হতে চাই। আমি ভাবছি তার যুক্তিটি কী ছিল
ইউংগুন

8
গাইডগুলি আবার স্ট্রাইক করেছে: নিক্সিং স্টাফ যা কার্যকর হতে পারে। ঠিক আছে যে আর হবে না।
জাভাদ্বা

3
পাইথনের বিষয়ে এটি আমি সবচেয়ে দুঃখজনক বিষয় দেখেছি।
এটিলিওএ

262

ব্যাখ্যা

পিইপি 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 পূর্ববর্তী প্যাকেজগুলি আমদানি করতে হবে , যেখানে এন হ'ল প্যারেন্ট ডিরেক্টরিগুলির সংখ্যা (স্ক্রিপ্টের ডিরেক্টরিটির সাথে সম্পর্কিত) যা মডিউলটি আমদানি করার জন্য অনুসন্ধান করা হবে।

সুতরাং,

  1. বর্তমান মডিউলটির Nth পূর্বসূরীর পিতামহিত ডিরেক্টরিতে যুক্ত করুনsys.path

  2. বর্তমান ফাইলের ডিরেক্টরিটি এখান থেকে সরান sys.path

  3. বর্তমান মডিউলটির সম্পূর্ণ-যোগ্যতাসম্পন্ন নাম ব্যবহার করে প্যারেন্ট মডিউলটি আমদানি করুন

  4. 2__package__ থেকে সম্পূর্ণ যোগ্যতার নাম সেট করুন

  5. আপেক্ষিক আমদানি সম্পাদন করুন

আমি সমাধান # 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: পরম আমদানি এবং সেটআপলগুলি ব্যবহার করুন

পদক্ষেপগুলি হ'ল -

  1. সুস্পষ্ট আপেক্ষিক আমদানিকে সমতুল্য পরম আমদানির সাথে প্রতিস্থাপন করুন

  2. 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 থেকে ফাইলগুলি ধার করতে এবং স্ট্যান্ড্যালোন.পি পরিবর্তন করতে যাচ্ছি :

  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
  2. নিখুঁত আমদানি দ্বারা আপেক্ষিক আমদানি প্রতিস্থাপন করুন:

    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 নিখুঁত আমদানি ব্যবহারের পরামর্শ দিচ্ছে, তবে কয়েকটি পরিস্থিতিতে স্পষ্টত আপেক্ষিক আমদানি গ্রহণযোগ্য:

নিখুঁত আমদানি সুপারিশ করা হয়, কারণ এগুলি সাধারণত বেশি পাঠযোগ্য এবং আরও ভাল আচরণ করার ঝোঁক রয়েছে (বা কমপক্ষে আরও ভাল ত্রুটির বার্তা দিন)। [...] তবে, সুস্পষ্ট আপেক্ষিক আমদানি নিখুঁত আমদানির একটি গ্রহণযোগ্য বিকল্প, বিশেষত যখন জটিল প্যাকেজ লেআউটগুলি নিয়ে কাজ করে যেখানে নিরঙ্কুশ আমদানি ব্যবহার করা অযথা ভার্চুজের হয়ে থাকে।


3
সমস্যাটি সমাধান করার জন্য __package__নাম থাকলে ম্যানুয়ালি সেট করা সম্ভব __main__?
পাওলো স্কার্ডাইন

ধন্যবাদ, সুন্দর উত্তর! আমি মডিউলটি ব্যবহার করে impমডিউলটি লোড করতে সক্ষম করেছিলাম এবং __package__সে অনুযায়ী সেট করেছিলাম তবে ফলাফলটি স্পষ্টভাবে একটি অ্যান্টি-প্যাটার্ন।
পাওলো স্কার্ডাইন

আমি ত্রুটি পেয়েছি AttributeError: 'PosixPath' object has no attribute 'path'
ব্যবহারকারী

দ্রুত উত্তর দেওয়ার জন্য ধন্যবাদ। আমি nltk প্যাকেজটি ব্যবহার করছি, আমি একটি ত্রুটি পেয়েছি: lt ফাইল "/usr/local/lib/python3.5/dist-packages/nltk/__init__.py", নল্ট.ডেকোরেটর থেকে <মডুল> মধ্যে 115 লাইন ডেকোরেটর আমদানি করুন, ফাইলকে স্মরণ করুন "/usr/local/lib/python3.5/dist-packages/nltk/decorators.py", 23 লাইন, <module> sys.path = [p এর জন্য sys.path- এ "nltk "পি-তে নেই] ফাইল" /usr/local/lib/python3.5/dist-packages/nltk/decorators.py ", 23 লাইন, <listcomp> sys.path = [পি মধ্যে sys.path এর জন্য p যদি" nltk "p এ নয়] প্রকারের ত্রুটি: 'পিক্সিকপথ' টাইপের যুক্তিটি পুনরাবৃত্তিযোগ্য নয়
user

1
আপনি ফাইল পাথ দ্বারা কোনও ফাইলও আমদানি করতে পারেন (আপেক্ষিকভাবেও): ডকস.পিথথন.আর
Ctrl-C

84

এটি আপনার প্যাকেজের __init__.py ফাইলের মধ্যে রাখুন :

# For relative imports to work in Python 3.6
import os, sys; sys.path.append(os.path.dirname(os.path.realpath(__file__)))

আপনার প্যাকেজটি ধরে নেওয়া এইরকম:

├── project
   ├── package
      ├── __init__.py
      ├── module1.py
      └── module2.py
   └── setup.py

এখন আপনার প্যাকেজে নিয়মিত আমদানি ব্যবহার করুন:

# in module2.py
from module1 import class1

এটি পাইথন 2 এবং 3 উভয় ক্ষেত্রেই কাজ করে।


আমরা যদি এটি হ'ল প্যাকেজ করি তবে এই কাজটি করে
অ্যালেক্স পুননেন

1
আমি আরও মনে করি এটি আরও ভোটের দাবিদার। এটিকে প্রতিটি রাখলে __init__.pyমূলত সমস্ত আপেক্ষিক ত্রুটি সমাধান হবে solve
frankliuao

3
আমি অন্যের পক্ষে কথা বলতে পারি না, তবে আমি পরিবর্তন এড়াতে ঝোঁক sys.pathথাকি কারণ আমি উদ্বিগ্ন এটি অন্যান্য কোডকে প্রভাবিত করতে পারে। (আংশিকরূপে এটি কারণ এটি কীভাবে কাজ করে তার জটিলতাগুলি আমি জানি না))
পিয়ানোজেমস

@ পিয়ানোজেমস আমি আপনার অর্থ কী তা আমি জানি, এটি (মনে হচ্ছে, অনেকগুলি ঘৃণার পরে) ম্যাজিক ফিক্সটি কিছুটা সহজ বলে মনে হচ্ছে। কিন্তু এটি কাজ করে. আগ্রহীরা যদি তাদের নেতিবাচক পার্শ্ব প্রতিক্রিয়াগুলি থেকে থাকে, তবে তাদের কাছ থেকে জেনে রাখা আগ্রহী।
জন

আমি এখন এটি ব্যবহার করছি: এবং এখনও পর্যন্ত খুব ভাল।
জাভাদবা

37

আমি এই ইস্যু মধ্যে দৌড়ে। একটি হ্যাক ওয়ার্কারআউন্ড নীচের মত একটি if / অন্য ব্লক মাধ্যমে আমদানি করা হয়:

#!/usr/bin/env python3
#myothermodule

if __name__ == '__main__':
    from mymodule import as_int
else:
    from .mymodule import as_int


# Exported function
def add(a, b):
    return as_int(a) + as_int(b)

# Test function for module  
def _test():
    assert add('1', '1') == 2

if __name__ == '__main__':
    _test()

29
এটি খুব সুন্দর সমাধান নয়। এছাড়াও, খালি except:খারাপ। except ImportError:পরিবর্তে ব্যবহার করুন!
থিফ মাস্টার

6
এটা SystemErrorএখানে। (পাই 3.4)
আভি

8
এটি কোনও ভয়ানক ধারণা নয়, তবে চেষ্টা / বাদ দিয়ে কোনটি আমদানি করা উচিত তা সনাক্ত করা ভাল। কিছু একটা if __name__ == '__main__': from mymod import as_int; else: from .mymod import as_int
পার্কিনস

@ পারকিন্স ভাল ... বেশিরভাগ ক্ষেত্রে তা হবে না । আমি মনে করি আপেক্ষিক আমদানি যদিও ব্যতিক্রম হতে পারে।
wizzwizz4

8

আশা করি, এটি কারওর জন্যই মূল্যবান হবে - আমি এখানে উপরের পোস্টের মতো আপেক্ষিক আমদানি সনাক্ত করার চেষ্টা করে অর্ধ ডজন স্ট্যাকওভারফ্লো পোস্ট দিয়েছিলাম। আমি প্রস্তাবিত হিসাবে সবকিছু সেট আপ করেছি কিন্তু আমি এখনও আঘাত করছিModuleNotFoundError: No module named 'my_module_name'

যেহেতু আমি কেবল স্থানীয়ভাবে উন্নয়ন করছিলাম এবং চারপাশে খেলছিলাম, তাই আমি কোনও setup.pyফাইল তৈরি / চালাইনি । আমিও দৃশ্যত আমার সেট ছিল না PYTHONPATH

আমি বুঝতে পেরেছিলাম যে পরীক্ষাগুলি মডিউলটির মতো একই ডিরেক্টরিতে ছিল যখন আমি যখন ছিলাম তখন আমার কোডটি চালিয়েছি, আমি আমার মডিউলটি খুঁজে পাইনি:

$ python3 test/my_module/module_test.py                                                                                                               2.4.0
Traceback (most recent call last):
  File "test/my_module/module_test.py", line 6, in <module>
    from my_module.module import *
ModuleNotFoundError: No module named 'my_module'

যাইহোক, যখন আমি স্পষ্টভাবে নির্দিষ্ট করে দিয়েছি যে জিনিসগুলি কাজ শুরু করেছিল:

$ PYTHONPATH=. python3 test/my_module/module_test.py                                                                                                  2.4.0
...........
----------------------------------------------------------------------
Ran 11 tests in 0.001s

OK

সুতরাং, যদি কেউ কয়েকটি প্রস্তাবনা ব্যবহার করে দেখে মনে হয় যে তাদের কোডটি সঠিকভাবে কাঠামোযুক্ত হয়েছে এবং এখনও নিজেকে একইরকম পরিস্থিতিতে আবিষ্কার করেন তবে নীচের মধ্যে দুটিই চেষ্টা করুন যদি আপনি বর্তমান ডিরেক্টরিটি আপনার পাইথনপথে এক্সপোর্ট না করেন:

  1. আপনার কোড চালান এবং স্পষ্টভাবে এর মতো পথ অন্তর্ভুক্ত করুন: $ PYTHONPATH=. python3 test/my_module/module_test.py
  2. কলিং এড়ানোর জন্য PYTHONPATH=., setup.pyনিম্নলিখিতগুলির মতো সামগ্রী সহ একটি ফাইল তৈরি করুন এবং python setup.py developmentপ্যাকেজগুলিতে যোগ করতে চালান :
# setup.py
from setuptools import setup, find_packages

setup(
    name='sample',
    packages=find_packages()
)

6

এটিকে কাজ করতে আমার মূল প্রকল্প ডিরেক্টরি থেকে পাইথন 3 চালানো দরকার।

উদাহরণস্বরূপ, যদি প্রকল্পের নিম্নলিখিত কাঠামো থাকে:

project_demo/
├── main.py
├── some_package/
   ├── __init__.py
   └── project_configs.py
└── test/
    └── test_project_configs.py

সমাধান

আমি ফোল্ডার প্রজেক্ট_ডেমো / এর ভিতরে পাইথন 3 চালাব এবং তারপরে একটি সম্পাদন করব

from some_package import project_configs

4

এই সমস্যা নিবারণ করার জন্য, আমি সঙ্গে একটি সমাধান উদ্ভাবন পুনঃপ্যাকেজ প্যাকেজ, যা কিছু সময়ের জন্য আমার জন্য কাজ করেছে। এটি lib পথে উপরের ডিরেক্টরিটি যুক্ত করে:

import repackage
repackage.up()
from mypackage.mymodule import myfunction

পুনঃস্থাপন একটি বুদ্ধিমান কৌশল (কল স্ট্যাক পরিদর্শন করা) ব্যবহার করে বিস্তৃত ক্ষেত্রে কাজ করে এমন আপেক্ষিক আমদানি করতে পারে।


এতদূর সহজ সমাধান! ধন্যবাদ!
কোডিংআইনসার্লাইক্লস

1
ধন্যবাদ! সেরা উত্তর দেওয়ার চেষ্টা করার পরিবর্তে, আমি একটি ব্যবহারযোগ্য উত্তর দেওয়ার চেষ্টা করেছি :-)
ফ্রেলেউ

3

যদি উভয় প্যাকেজগুলি আপনার আমদানির পথে (sys.path) থাকে এবং আপনি যে মডিউল / শ্রেণিটি চান তা উদাহরণস্বরূপ / example.py হয়, তবে আপেক্ষিক আমদানি ছাড়াই শ্রেণিতে অ্যাক্সেস করার চেষ্টা করুন:

from example.example import fkt

1

আমি মনে করি আপনার মডিউলটির জন্য একটি প্যাকেজ তৈরি করা সবচেয়ে ভাল সমাধান: এটি কীভাবে করবেন সে সম্পর্কে আরও তথ্য এখানে

আপনার কাছে একবার প্যাকেজ হয়ে গেলে আপেক্ষিক আমদানির বিষয়ে চিন্তা করার দরকার নেই, আপনি কেবল পরম আমদানি করতে পারেন।


0

আমার একই রকম সমস্যা ছিল: আমার একটি লিনাক্স পরিষেবা এবং সিজিআই প্লাগইন দরকার যা সহযোগিতা করার জন্য সাধারণ ধ্রুবক ব্যবহার করে। এটি করার 'প্রাকৃতিক' উপায়টি হ'ল প্যাকেজটির init .py এ তাদের স্থাপন করা যায় , তবে আমি -m পরামিতি দিয়ে সিজি প্লাগইন শুরু করতে পারি না।

আমার চূড়ান্ত সমাধান উপরের সমাধান # 2 এর মতোই ছিল:

import sys
import pathlib as p
import importlib

pp = p.Path(sys.argv[0])
pack = pp.resolve().parent

pkg = importlib.import_module('__init__', package=str(pack))

অসুবিধাটি হ'ল আপনাকে অবশ্যই পকেজি সহ ধ্রুবকগুলি (বা সাধারণ ফাংশন) উপসর্গ করতে হবে:

print(pkg.Glob)
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.