পাইথনে আপেক্ষিক আমদানি কীভাবে করবেন?


527

এই ডিরেক্টরি কাঠামোর কল্পনা করুন:

app/
   __init__.py
   sub1/
      __init__.py
      mod1.py
   sub2/
      __init__.py
      mod2.py

আমি কোডিং করছি mod1, এবং এ থেকে আমার কিছু আমদানি করা দরকার mod2। আমি এটা কিভাবে করব?

আমি চেষ্টা করেছি from ..sub2 import mod2তবে আমি একটি "নন-প্যাকেজে আপেক্ষিক আমদানির চেষ্টা করা" পাচ্ছি।

আমি আশেপাশে গুগল করেছিলাম তবে কেবল " sys.pathম্যানিপুলেশন" হ্যাকগুলি পেয়েছি । কোন পরিষ্কার উপায় নেই?


সম্পাদনা করুন: আমার সমস্ত __init__.pyবর্তমানে শূন্য

Edit2: আমি এই কাজ করতে, কারণ sub2 শ্রেণীর সাব প্যাকেজ (জুড়ে ভাগ রয়েছে চেষ্টা করছি sub1, subX, ইত্যাদি)।

সম্পাদনা 3: আমি যে আচরণটি সন্ধান করছি তা পিইপি 366-তে বর্ণিত একই রকম (ধন্যবাদ জন বি)


8
আমি তোমার প্রশ্ন আপডেট সুপারিশ এটা আরো স্পষ্ট যে আপনি বর্ণনা করছি ইস্যু PEP 366. সুরাহা করতে
জন বি

2
এটি একটি দীর্ঘ বাতাসযুক্ত ব্যাখ্যা তবে এখানে দেখুন: stackoverflow.com/a/10713254/1267156 আমি একটি খুব অনুরূপ প্রশ্নের উত্তর দিয়েছি। গত রাত পর্যন্ত আমার একই সমস্যা ছিল।
Sevvy325

3
যারা একটি স্বেচ্ছাচারী পথে অবস্থিত একটি মডিউল লোড করতে চান তাদের জন্য এটি দেখুন: স্ট্যাকওভারফ্লো.com
এভেজেনি সার্জিভ

2
সম্পর্কিত নোটে, পাইথন 3 আমদানির ডিফল্ট হ্যান্ডলিংকে ডিফল্টরূপে পরম হয়ে উঠবে; আপেক্ষিক আমদানি স্পষ্টভাবে নির্দিষ্ট করতে হবে।
রস 23

উত্তর:


337

প্রত্যেকে কেবলমাত্র প্রশ্নের উত্তর দেওয়ার পরিবর্তে আপনাকে কী করা উচিত তা বলতে চাইছে।

সমস্যাটি হ'ল আপনি মডিউলটি '__main__' হিসাবে দোভাষী হিসাবে একটি যুক্তি হিসাবে mod1.py পাস করে মডিউলটি চালাচ্ছেন।

পিইপি 328 থেকে :

আপেক্ষিক আমদানিগুলি প্যাকেজ শ্রেণিবিন্যাসের ক্ষেত্রে মডিউলটির অবস্থান নির্ধারণ করতে মডিউলের __name__ বৈশিষ্ট্য ব্যবহার করে। যদি মডিউলটির নামটিতে কোনও প্যাকেজ তথ্য না থাকে (যেমন এটি '__main__' সেট করা থাকে) তবে আপেক্ষিক আমদানিগুলি সমাধান করা হয় যেন মডিউলটি কোনও শীর্ষ স্তরের মডিউল, ফাইল মডিউলটি প্রকৃতপক্ষে ফাইল সিস্টেমে অবস্থিত কিনা তা নির্বিশেষে।

পাইথন ২.6-এ, তারা মূল মডিউলটির সাথে সম্পর্কিত মডিউলগুলি উল্লেখ করার ক্ষমতা যুক্ত করছে। পিইপি 366 পরিবর্তনের বর্ণনা দেয়।

আপডেট : নিক কোঘলানের মতে, প্রস্তাবিত বিকল্প হ'ল -m সুইচ ব্যবহার করে প্যাকেজের অভ্যন্তরে মডিউল চালানো।


2
এখানে উত্তরটি আপনার প্রোগ্রামের প্রতিটি এন্ট্রি পয়েন্টে sys.path এর সাথে বিশৃঙ্খলা জড়িত। আমার ধারণা এটি করার একমাত্র উপায়।
নিক retallack

76
প্রস্তাবিত বিকল্প হ'ল প্যাকেজগুলির ভিতরে মডিউলগুলি -mতাদের ফাইলের নামটি সরাসরি উল্লেখ না করে স্যুইচ ব্যবহার করে চালানো inside
ncoghlan

127
আমি বুঝতে পারি না: এখানে উত্তর কোথায়? এই জাতীয় ডিরেক্টরি কাঠামোর মধ্যে একটি মডিউল কীভাবে আমদানি করতে পারে?
টম

27
@ টম: এই পরিস্থিতিতে, Mod1 হবে from sub2 import mod2। তারপরে, অ্যাপের মধ্যে থেকে মোড 1 চালাতে, করুন python -m sub1.mod1
জিওনগ চিয়ামিভ

11
@ জিওনজিচামিওভ: এর অর্থ কি আপনার পাইথন যদি কোনও অ্যাপ্লিকেশনটিতে এম্বেড থাকে তবে আপনি পাইথন কমান্ড লাইন সুইচগুলিতে অ্যাক্সেস পাবেন না?
LarsH

130

আমার জন্য কাজ করে এমন সমাধানটি এখানে:

আমি সেই হিসাবে আপেক্ষিক আমদানি করি from ..sub2 import mod2 এবং তারপরে, যদি আমি চালাতে চাই mod1.pyতবে আমি পিতামাতার ডিরেক্টরিতে appগিয়ে পাইথন-এম সুইচটি ব্যবহার করে মডিউলটি চালিত করি python -m app.sub1.mod1

আপেক্ষিক আমদানির ক্ষেত্রে এই সমস্যাটি হওয়ার কারণটির আসল কারণটি হ'ল আপেক্ষিক আমদানিগুলি __name__মডিউলটির সম্পত্তি নিয়ে কাজ করে । যদি মডিউলটি সরাসরি চালিত হয় তবে __name__সেট করা থাকে __main__এবং এতে প্যাকেজ কাঠামোর কোনও তথ্য থাকে না। এবং, পাইথন relative import in non-packageত্রুটি সম্পর্কে অভিযোগ কেন ।

সুতরাং, -m স্যুইচ ব্যবহার করে আপনি পাইথনকে প্যাকেজ কাঠামোর তথ্য সরবরাহ করেন, যার মাধ্যমে এটি আপেক্ষিক আমদানিগুলি সফলভাবে সমাধান করতে পারে।

আপেক্ষিক আমদানি করার সময় আমি অনেকবার এই সমস্যার মুখোমুখি হয়েছি। এবং, পূর্ববর্তী সমস্ত উত্তরগুলি পড়ার পরেও, সমস্ত ফাইলগুলিতে বয়লারপ্লিট কোড লাগানোর প্রয়োজন ছাড়াই, পরিষ্কার পদ্ধতিতে কীভাবে এটি সমাধান করা যায় তা আমি এখনও বুঝতে সক্ষম হয়েছি। (যদিও কিছু মন্তব্য সত্যই সহায়ক ছিল, @ কনোগলান এবং @ জিওনজিচিয়ামিওভকে ধন্যবাদ)

আশা করি এটি এমন কাউকে সহায়তা করে যা আপেক্ষিক আমদানি সমস্যার সাথে লড়াই করে, কারণ পিইপি দিয়ে যাওয়া সত্যিই মজাদার নয়।


9
সেরা উত্তর আইএমএইচও: কেবল ওপির কেন বিষয়টি ছিল তা ব্যাখ্যা করে না, তবে তার মডিউলগুলি যেভাবে আমদানি করে সেগুলি পরিবর্তন না করেই এটি সমাধানের উপায় খুঁজে বের করে । সামগ্রিকভাবে, ওপির আপেক্ষিক আমদানি ভাল ছিল। অপরাধী হ'ল বাইরের প্যাকেজগুলিতে অ্যাক্সেসের অভাব ছিল যখন সরাসরি স্ক্রিপ্ট হিসাবে চলছিল, কিছু -mসমাধানের জন্য তৈরি করা হয়েছিল।
MestreLion

26
এছাড়াও খেয়াল করুন: এই উত্তরটি প্রশ্নের পরে 5 বছর পরে ছিল। এই বৈশিষ্ট্যগুলি তখন পাওয়া যায় নি।
জেরেমিকুন

1
আপনি যদি একই ডিরেক্টরি থেকে কোনও মডিউল আমদানি করতে চান তবে আপনি করতে পারেন from . import some_module
রোটারেটি

124
main.py
setup.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       module_a.py
    package_b/ ->
       __init__.py
       module_b.py
  1. আপনি চালান python main.py
  2. main.py না: import app.package_a.module_a
  3. module_a.py না import app.package_b.module_b

বিকল্পভাবে 2 বা 3 ব্যবহার করতে পারে: from app.package_a import module_a

এটি appআপনার পাইথনপথে যতক্ষণ থাকবে ততক্ষণ কাজ করবে । main.pyতখন কোথাও হতে পারে।

সুতরাং আপনি setup.pyসম্পূর্ণ অ্যাপ্লিকেশন প্যাকেজ এবং সাব-প্যাকেজগুলি টার্গেট সিস্টেমের পাইথন ফোল্ডারগুলিতে অনুলিপি main.pyকরতে এবং সিস্টেমের স্ক্রিপ্ট ফোল্ডারগুলিতে একটি লিখুন।


3
দুর্দান্ত উত্তর। পাইথনপথে প্যাকেজ ইনস্টল না করে সেভাবে আমদানির কোনও উপায় আছে কি?
uraরহাম

4
অতিরিক্ত পড়ার পরামর্শ দেওয়া হয়েছে: blog.habnab.it/blog/2013/07/21/python-packages-and-you
nosklo

6
তারপরে, একদিন, অ্যাপের নামটি টেস্ট_এ্যাপে পরিবর্তন করতে হবে change কি হবে? আপনাকে সমস্ত উত্স কোড পরিবর্তন করতে হবে, অ্যাপ্লিকেশন.প্যাকেজ_বি.মডিউল_বি -> পরীক্ষা_অ্যাপ.প্যাকেজ_বি.মডিউল_বি আমদানি করতে হবে। এটি একেবারে খারাপ অভ্যাস ... এবং আমাদের প্যাকেজের মধ্যে আপেক্ষিক আমদানি ব্যবহার করার চেষ্টা করা উচিত।
স্পাইবদাই

49

"গিডো প্যাকেজটির মধ্যে চলমান স্ক্রিপ্টগুলিকে অ্যান্টি-প্যাটার্ন হিসাবে দেখায়" ( পিইপি -3122 প্রত্যাখ্যান )

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


10
দ্রষ্টব্য: ইতিমধ্যে উল্লিখিত পেপ-3666 ( পেপ -১২২২ হিসাবে একই সময়ে তৈরি করা হয়েছে ) একই ক্ষমতা সরবরাহ করে তবে একটি ভিন্ন পশ্চাদপদ-সামঞ্জস্যপূর্ণ প্রয়োগ ব্যবহার করে, যদি আপনি কোনও স্ক্রিপ্ট হিসাবে প্যাকেজের অভ্যন্তরে কোনও মডিউল চালাতে চান এবং সুস্পষ্ট আপেক্ষিক আমদানি ব্যবহার করতে চান এটিতে আপনি এটি -mসুইচ ব্যবহার করে চালাতে পারেন: python -m app.sub1.mod1বা app.sub1.mod1.main()একটি শীর্ষ-স্তরের স্ক্রিপ্ট থেকে উদ্বোধন করতে পারেন (উদাহরণস্বরূপ, সেটআপলগুলিতে প্রবেশ_পয়েন্টগুলি সেটআপ.পিতে সংজ্ঞায়িত)।
jfs

সেটআপটুলস এবং এন্ট্রি পয়েন্টগুলি ব্যবহারের জন্য +1 - স্ক্রিপ্টগুলি সেটআপ করার উপযুক্ত উপায় যা বাইরে থেকে চালিত হবে, একটি সু-সংজ্ঞায়িত স্থানে,
অজস্রভাবে পাইথনপথ

38

এটি 100% সমাধান করা হয়েছে:

  • অ্যাপ্লিকেশন /
    • main.py
  • সেটিংস/
    • local_setings.py

অ্যাপ্লিকেশন / প্রধান.পায়িতে সেটিংস / স্থানীয়_সেটিং.পিটি আমদানি করুন:

main.py:

import sys
sys.path.insert(0, "../settings")


try:
    from local_settings import *
except ImportError:
    print('No Import')

2
ধন্যবাদ! সমস্ত পিপিএল আমাকে স্ক্রিপ্টের মধ্যে কীভাবে এটি সমাধান করতে হয় তা বলার পরিবর্তে আমাকে আমার স্ক্রিপ্টটি অন্যভাবে চালাতে বাধ্য করছিল। তবে আমাকে ব্যবহারের জন্য কোডটি পরিবর্তন করতে হবে sys.path.insert(0, "../settings")এবং তারপরেfrom local_settings import *
ভিট বার্নাটিক

25
def import_path(fullpath):
    """ 
    Import a file with full path specification. Allows one to
    import from anywhere, something __import__ does not do. 
    """
    path, filename = os.path.split(fullpath)
    filename, ext = os.path.splitext(filename)
    sys.path.append(path)
    module = __import__(filename)
    reload(module) # Might be out of date
    del sys.path[-1]
    return module

আমি এই স্নিপেটটি পাথ থেকে মডিউলগুলি আমদানি করতে ব্যবহার করছি, আশা করি এটি সহায়তা করে


2
আমি ইমপি মডিউলের সাথে মিলিত এই স্নিপেটটি ব্যবহার করছি (এখানে [1] এখানে বর্ণিত হয়েছে) দুর্দান্ত প্রভাব হিসাবে। [1]: stackoverflow.com/questions/1096216/...
Xiong থেকে Chiamiov

7
সম্ভবত, sys.path.append (পাথ) sys.path.insert (0, পাথ) দ্বারা প্রতিস্থাপন করা উচিত, এবং sys.path [-1] sys.path [0] দ্বারা প্রতিস্থাপন করা উচিত। অন্যথায় ফাংশনটি ভুল মডিউলটি আমদানি করবে, যদি ইতিমধ্যে অনুসন্ধানের পথে একই নামের একটি মডিউল থাকে। উদাহরণস্বরূপ, যদি বর্তমান ডিয়ারে "some.py" থাকে তবে আমদানি_পথ ("/ আমদানি / some.py") ভুল ফাইলটি আমদানি করবে।
অ্যালেক্স চে

আমি রাজী! কখনও কখনও অন্যান্য আপেক্ষিক আমদানি অগ্রাধিকার দেবে। Sys.path.insert
iElectric

এক্স ইমপোর্ট y (বা *) থেকে আপনি কীভাবে তার আচরণের প্রতিলিপি করবেন?
লেভেস্ক

এটি পরিষ্কার নয়, ওপি সমস্যা সমাধানের জন্য দয়া করে এই স্ক্রিপ্টটির সম্পূর্ণ ব্যবহার নির্দিষ্ট করুন।
mrgloom

21

nosklo'sউদাহরণ সহ উত্তর ব্যাখ্যা

দ্রষ্টব্য: সমস্ত __init__.pyফাইল খালি আছে।

main.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       fun_a.py
    package_b/ ->
       __init__.py
       fun_b.py

অ্যাপ্লিকেশন / package_a / fun_a.py

def print_a():
    print 'This is a function in dir package_a'

অ্যাপ্লিকেশন / package_b / fun_b.py

from app.package_a.fun_a import print_a
def print_b():
    print 'This is a function in dir package_b'
    print 'going to call a function in dir package_a'
    print '-'*30
    print_a()

main.py

from app.package_b import fun_b
fun_b.print_b()

যদি আপনি $ python main.pyএটি চালান ফিরে আসে:

This is a function in dir package_b
going to call a function in dir package_a
------------------------------
This is a function in dir package_a
  • main.py করে: from app.package_b import fun_b
  • মজা_বি.পি করেন from app.package_a.fun_a import print_a

ফোল্ডারে ফাইলটি ফোল্ডারে package_bব্যবহৃত ফাইল package_a, যা আপনি চান তা। রাইট ??


12

এটি দুর্ভাগ্যক্রমে একটি sys.path হ্যাক, তবে এটি বেশ ভালভাবে কাজ করে।

আমি অন্য স্তরের সাথে এই সমস্যার মুখোমুখি হয়েছি: আমার কাছে ইতিমধ্যে নির্দিষ্ট নামের একটি মডিউল ছিল, তবে এটি ছিল ভুল মডিউল।

আমি যা করতে চেয়েছিলাম তা হ'ল (আমি যে মডিউলটি থেকে কাজ করছিলাম তা ছিল মডিউল 3):

mymodule\
   __init__.py
   mymodule1\
      __init__.py
      mymodule1_1
   mymodule2\
      __init__.py
      mymodule2_1


import mymodule.mymodule1.mymodule1_1  

মনে রাখবেন যে আমি ইতিমধ্যে মাইমডিউল ইনস্টল করেছি, তবে আমার ইনস্টলেশনটিতে আমার কাছে "মাইমডিউল 1" নেই

এবং আমি একটি আমদানি করবো কারণ এটি আমার ইনস্টল করা মডিউলগুলি থেকে আমদানির চেষ্টা করেছিল।

আমি একটি sys.path.append করার চেষ্টা করেছি, এবং এটি কার্যকর হয়নি। কাজটি কী ছিল তা ছিল sys.path.insert

if __name__ == '__main__':
    sys.path.insert(0, '../..')

হ্যাক তাই ধরণের, কিন্তু এটি সব কাজ পেয়েছে! সুতরাং মনে রাখবেন, আপনি যদি অন্য সিদ্ধান্তগুলি ওভাররাইড করার সিদ্ধান্ত নিতে চান তবে এটির কাজ করতে আপনাকে sys.path.insert (0, पथ নাম) ব্যবহার করতে হবে! এটি আমার জন্য খুব হতাশাব্যঞ্জক স্টিকিং পয়েন্ট ছিল, প্রচুর লোক sys.path এ "অ্যাপেন্ড" ফাংশনটি ব্যবহার করতে বলে, তবে আপনার যদি ইতিমধ্যে সংজ্ঞা দেওয়া মডিউল থাকে তবে এটি কাজ করে না (আমি এটি খুব আশ্চর্যজনক আচরণ বলে মনে করি)


sys.path.append('../')আমার জন্য সূক্ষ্মভাবে কাজ করে (পাইথন ৩.৩.২)
নিস্টার

আমি মনে করি এটি ঠিক আছে, কারণ এটি হ্যাককে এক্সিকিউটেবলের কাছে স্থানীয়করণ করে এবং আপনার প্যাকেজগুলির উপর নির্ভর করে এমন অন্যান্য মডিউলগুলিকে প্রভাবিত করে না।
টম রাসেল

10

আমাকে এখানে আমার নিজস্ব রেফারেন্সের জন্য এখানে রাখি। আমি জানি যে এটি পাইথন কোডটি ভাল নয়, তবে আমি যে প্রকল্পে কাজ করছি তার জন্য আমার একটি স্ক্রিপ্ট দরকার ছিল এবং আমি স্ক্রিপ্টটি একটি scriptsডিরেক্টরিতে রাখতে চেয়েছিলাম ।

import os.path
import sys
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))

9

@ ইভজেনিসার্জেভ যেমন ওপিকে দেওয়া মন্তব্যে বলেছেন, আপনি একটি .pyফাইল থেকে একটি স্বেচ্ছাসেবী অবস্থানে কোড আমদানি করতে পারেন :

import imp

foo = imp.load_source('module.name', '/path/to/file.py')
foo.MyClass()

এটি এই এসও উত্তর থেকে নেওয়া হয়েছে ।



2

পাইথন ডক থেকে ,

পাইথন 2.5 তে, আপনি কোনও from __future__ import absolute_importনির্দেশিকা ব্যবহার করে আমদানির আচরণকে নিখুঁত আমদানিতে স্যুইচ করতে পারেন । এই পরম-আমদানি আচরণটি ভবিষ্যতের সংস্করণে সম্ভবত ডিফল্ট হয়ে উঠবে (সম্ভবত পাইথন ২.7)। একবার পরম আমদানি ডিফল্ট হয়ে গেলে import stringসর্বদা মানক পাঠাগারের সংস্করণটি খুঁজে পাবেন। এটা তোলে প্রস্তাব দেওয়া হচ্ছে ব্যবহারকারীরা, তাই এটি লেখার শুরু করা বাঞ্ছনীয় যতটা সম্ভব পরম আমদানির ব্যবহার আরম্ভ করা উচিত from pkg import stringআপনার কোডে


1

আমি খুঁজে পেয়েছি যে শীর্ষস্থানীয় ফোল্ডারে "পাইথনপথ" পরিবেশের পরিবর্তনশীল সেট করা আরও সহজ:

bash$ export PYTHONPATH=/PATH/TO/APP

তারপর:

import sub1.func1
#...more import

অবশ্যই, পাইথনপথ "গ্লোবাল", তবে এটি এখনও আমার জন্য সমস্যা উত্থাপন করেনি।


এটি virtualenvআপনার আমদানির বিবৃতিগুলি কীভাবে পরিচালনা করতে দেয় তা মূলত এটি ।
বাইসোর

1

জন বি যা বলেছিলেন তার উপরে __package__, পরিবর্তনগুলি পরিবর্তনের পরিবর্তে ভেরিয়েবলটি নির্ধারণ করা উচিত__main__ যা যা অন্যান্য জিনিসগুলিকে স্ক্রু করতে পারে বলে মনে হয়। তবে যতদূর আমি পরীক্ষা করতে পারি, এটি সম্পূর্ণরূপে এটির মতো কাজ করে না।

আমার একই সমস্যা রয়েছে এবং পিইপি 328 বা 366 উভয়ই সমস্যার সম্পূর্ণ সমাধান করতে পারে না, কারণ উভয়ই দিনের শেষে প্যাকেজের প্রধান অন্তর্ভুক্ত করতে হবে sys.path , যতদূর আমি বুঝতে পারি।

আমার আরও উল্লেখ করা উচিত যে আমি সেই স্ট্রিংগুলিকে কীভাবে সেই ভেরিয়েবলগুলির মধ্যে ফর্ম্যাট করতে হবে তা বিন্যাস করতে পারি না। এটা "package_head.subfolder.module_name"নাকি?


0

আপনাকে মডিউলটির পথটি এতে যুক্ত করতে হবে PYTHONPATH:

export PYTHONPATH="${PYTHONPATH}:/path/to/your/module/"

1
এই মোটামুটিভাবে সাধিত হিসাবে একই sys.path, যেহেতু sys.pathথেকে সক্রিয়া পরারPYTHONPATH
Joril

@ জোরিল এটি সঠিক তবে sys.pathউত্স কোডে হার্ডকড করা দরকার PYTHONPATHযার বিপরীতে পরিবেশের পরিবর্তনশীল এবং এটি রফতানি করা যেতে পারে।
জাইর্গোস মাইরিয়ানথাস
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.