দুটি মডিউল একে অপরকে আমদানি করলে কী হবে?
সমস্যাটি সাধারণ করতে, পাইথনের চক্রীয় আমদানি সম্পর্কে কী?
দুটি মডিউল একে অপরকে আমদানি করলে কী হবে?
সমস্যাটি সাধারণ করতে, পাইথনের চক্রীয় আমদানি সম্পর্কে কী?
উত্তর:
গত বছর comp.lang.python এ এ নিয়ে সত্যিই একটি ভাল আলোচনা হয়েছিল । এটি আপনার প্রশ্নের সুন্দর উত্তর দেয়।
আমদানি সত্যিই বেশ সোজা। শুধু নিম্নলিখিত মনে রাখবেন:
'ইম্পোর্ট' এবং 'এক্সএক্সএক্সএক্স ইম্পোর্ট ইয়ে' থেকে এক্সিকিউটেবল স্টেটমেন্ট। চলমান প্রোগ্রামটি line লাইনে পৌঁছালে তারা কার্যকর করে।
যদি কোনও মডিউল sys.modules এ না থাকে, তবে একটি আমদানি sys.modules এ নতুন মডিউল এন্ট্রি তৈরি করে এবং তারপরে মডিউলটিতে কোডটি কার্যকর করে। কার্যকর করা শেষ না হওয়া পর্যন্ত এটি কলিং মডিউলে নিয়ন্ত্রণ ফিরিয়ে দেয় না।
যদি মডিউলটি সিস.মডিউলগুলিতে বিদ্যমান থাকে তবে কোনও আমদানি কেবল কার্যকরভাবে সম্পন্ন হয়েছে কিনা তা মডিউলটি দেয়। এই কারণেই সাইক্লিক আমদানি মডিউলগুলি আংশিক খালি মনে হতে পারে।
অবশেষে, নির্বাহী স্ক্রিপ্টটি __main__ নামক একটি মডিউলে চলে, তার নিজের নামে স্ক্রিপ্টটি আমদানি করা __main__ এর সাথে সম্পর্কযুক্ত একটি নতুন মডিউল তৈরি করবে।
অনেকগুলি একসাথে নিয়ে যান এবং মডিউলগুলি আমদানি করার সময় আপনার কোনও আশ্চর্য হওয়া উচিত নয়।
আপনি যদি import foo
ভিতরে bar
এবং import bar
ভিতরে করেন তবে foo
এটি দুর্দান্ত কাজ করবে। আসলে কোনও কিছু চলার সময় উভয় মডিউল পুরোপুরি লোড হয়ে যাবে এবং একে অপরের সাথে উল্লেখ থাকবে।
সমস্যাটি হ'ল পরিবর্তে আপনি যখন করেন from foo import abc
এবং করেন from bar import xyz
। কারণ এখন প্রতিটি মডিউলকে আমদানি করার আগেই অন্য মডিউলটি ইতিমধ্যে আমদানি করা প্রয়োজন (যাতে আমরা যে নামটি আমদানি করছি তা বিদ্যমান রয়েছে)।
from foo import *
এবং from bar import *
ভাল কাজ করবে।
from x import y
, এবং তবুও বিজ্ঞপ্তি আমদানির ত্রুটিটি পেয়েছে
import
বিবৃতি কার্যকর করা হয়। সুতরাং এটি ত্রুটিযুক্ত হবে না তবে আপনি যে ভেরিয়েবলগুলি প্রত্যাশা করেছেন তা পেতে পারেন না।
from foo import *
এবং from bar import *
, এর মধ্যে কার্যকর করা সমস্ত foo
কিছুই প্রাথমিক পর্যায়ে রয়েছে bar
এবং এর আসল ফাংশনগুলি bar
এখনও সংজ্ঞায়িত হয়নি ...
চক্রীয় আমদানি সমাপ্ত হয়, তবে আপনাকে মডিউল শুরুর সময় চক্রাকারে-আমদানি করা মডিউলগুলি ব্যবহার না করার বিষয়ে সতর্কতা অবলম্বন করা উচিত।
নিম্নলিখিত ফাইলগুলি বিবেচনা করুন:
a.py:
print "a in"
import sys
print "b imported: %s" % ("b" in sys.modules, )
import b
print "a out"
b.py:
print "b in"
import a
print "b out"
x = 3
আপনি যদি a.py কার্যকর করেন তবে আপনি নিম্নলিখিতগুলি পাবেন:
$ python a.py
a in
b imported: False
b in
a in
b imported: True
a out
b out
a out
বি.পি.-এর দ্বিতীয় আমদানিতে (দ্বিতীয়টিতে a in
) পাইথন ইন্টারপ্রেটার b
আবার আমদানি করে না , কারণ এটি মডিউল ডেকে ইতিমধ্যে বিদ্যমান।
আপনি যদি মডিউল শুরুর সময় b.x
থেকে অ্যাক্সেস করার চেষ্টা করেন তবে আপনি a
একটি পাবেন AttributeError
।
নিম্নলিখিত লাইন এতে যুক্ত করুন a.py
:
print b.x
তারপরে, আউটপুটটি হ'ল:
$ python a.py
a in
b imported: False
b in
a in
b imported: True
a out
Traceback (most recent call last):
File "a.py", line 4, in <module>
import b
File "/home/shlomme/tmp/x/b.py", line 2, in <module>
import a
File "/home/shlomme/tmp/x/a.py", line 7, in <module>
print b.x
AttributeError: 'module' object has no attribute 'x'
এটি কারণ আমদানিতে মডিউলগুলি কার্যকর করা হয় এবং সেই সময়ে b.x
অ্যাক্সেস করা হয়, লাইনটি x = 3
এখনও কার্যকর করা হয়নি যা কেবল পরে ঘটবে b out
।
__name__
পরিবর্তে ব্যবহার করেন তবে এই উত্তরটি অনেক উপকৃত হবে 'a'
। শুরুতে, আমি সম্পূর্ণ বিভ্রান্ত হয়ে পড়েছিলাম কেন একটি ফাইল দু'বার কার্যকর করা হবে।
অন্যান্য উত্তরগুলির বর্ণনা হিসাবে এই ধরণটি অজগরটিতে গ্রহণযোগ্য:
def dostuff(self):
from foo import bar
...
যা যখন ফাইলটি অন্য মডিউল দ্বারা আমদানি করা হয় তখন আমদানির বিবৃতি কার্যকর করতে এড়াতে পারে। একটি লজিকাল বিজ্ঞপ্তি নির্ভরতা থাকলেই এটি ব্যর্থ হবে।
বেশিরভাগ সার্কুলার আমদানি আসলে যৌক্তিক বিজ্ঞপ্তি আমদানি নয় বরং ImportError
ত্রুটি বাড়াতে import()
ডেকে আনা হয় কারণ ডাকা হওয়ার সময় পুরো ফাইলটির শীর্ষ স্তরের বিবৃতি মূল্যায়ন করে।
ImportErrors
যদি আপনি ইতিমধ্যে ইতিমধ্যে আপনার আমদানি চান তবে এগুলি প্রায়শই এড়ানো যায় :
এই বিজ্ঞপ্তি আমদানি বিবেচনা করুন:
# profiles/serializers.py
from images.serializers import SimplifiedImageSerializer
class SimplifiedProfileSerializer(serializers.Serializer):
name = serializers.CharField()
class ProfileSerializer(SimplifiedProfileSerializer):
recent_images = SimplifiedImageSerializer(many=True)
# images/serializers.py
from profiles.serializers import SimplifiedProfileSerializer
class SimplifiedImageSerializer(serializers.Serializer):
title = serializers.CharField()
class ImageSerializer(SimplifiedImageSerializer):
profile = SimplifiedProfileSerializer()
ডেভিড বিজলিজ থেকে দুর্দান্ত টক মডিউল এবং প্যাকেজগুলি: লাইভ এবং লেট ডাই! - পাইকন 2015 , 1:54:00
এখানে পাইথনের বৃত্তাকার আমদানি মোকাবেলার একটি উপায়:
try:
from images.serializers import SimplifiedImageSerializer
except ImportError:
import sys
SimplifiedImageSerializer = sys.modules[__package__ + '.SimplifiedImageSerializer']
এটি আমদানির চেষ্টা করে SimplifiedImageSerializer
এবং যদি ImportError
উত্থাপিত হয়, কারণ এটি ইতিমধ্যে আমদানি করা হয়েছে, এটি এটিকে আমদানি ক্যাশে থেকে টানবে।
পিএস: আপনাকে ডেভিড বেজলির কণ্ঠে এই পুরো পোস্টটি পড়তে হবে।
আমি এখানে একটি উদাহরণ পেয়েছি যা আমাকে আঘাত করেছে!
foo.py
import bar
class gX(object):
g = 10
bar.py
from foo import gX
o = gX()
main.py
import foo
import bar
print "all done"
কমান্ড লাইনে: y পাইথন মেইন.পি
Traceback (most recent call last):
File "m.py", line 1, in <module>
import foo
File "/home/xolve/foo.py", line 1, in <module>
import bar
File "/home/xolve/bar.py", line 1, in <module>
from foo import gX
ImportError: cannot import name gX
import bar
মধ্যে foo.py
থেকে শেষ
bar
এবং foo
উভয়ই অবশ্যই ব্যবহার করে gX
তবে 'পরিষ্কার' সমাধানটি gX
অন্য মডিউলে রাখা এবং উভয়ই রাখা foo
এবং bar
সেই মডিউলটি আমদানি করা। (কোনও লুক্কায়িত শব্দার্থ নির্ভরতা নেই এই অর্থে পরিচ্ছন্ন
bar
এমনকি gX
ফুওকেও খুঁজে পাচ্ছে না। বিজ্ঞপ্তি আমদানি নিজেই সূক্ষ্ম, তবে এটি gX
যখন আমদানি করা হয় তখন এটি সংজ্ঞায়িত হয় না।
মডিউল a.py:
import b
print("This is from module a")
মডিউল বি.পি.
import a
print("This is from module b")
"মডিউল এ" চালানো আউটপুট আসবে:
>>>
'This is from module a'
'This is from module b'
'This is from module a'
>>>
বিজ্ঞপ্তি আমদানির কারণে অনন্তর আউটপুট দেওয়ার সময় এটি এই 3 টি লাইন আউটপুট দেয়। "মডিউল এ" চালানোর সময় লাইনে কী ঘটে তা এখানে তালিকাভুক্ত করা হয়েছে:
import b
। সুতরাং এটি মডিউল খ পরিদর্শন করবেimport a
। সুতরাং এটি মডিউলটি পরিদর্শন করবে aimport b
তবে নোট করুন যে এই লাইনটি আর কার্যকর করা হবে না , কারণ পাইথনের প্রতিটি ফাইল কেবল একবারের জন্য একটি আমদানি লাইন চালায়, এটি কোথায় বা কখন কার্যকর হয় তা বিবেচ্য নয়। সুতরাং এটি পরবর্তী লাইনে চলে যাবে এবং মুদ্রণ করবে "This is from module a"
।"This is from module b"
"This is from module a"
এবং প্রোগ্রামটি শেষ হবে।আমি এখানে পাইথনারের উত্তরটির সাথে সম্পূর্ণ সম্মত। তবে আমি এমন কিছু কোডে হোঁচট খেয়েছি যা বিজ্ঞপ্তি আমদানিতে ত্রুটিযুক্ত ছিল এবং ইউনিট পরীক্ষা যোগ করার চেষ্টা করার সময় সমস্যার সৃষ্টি করেছিল। সুতরাং সবকিছু পরিবর্তন না করে দ্রুত প্যাচ করার জন্য আপনি গতিশীল আমদানি করে সমস্যাটি সমাধান করতে পারেন।
# Hack to import something without circular import issue
def load_module(name):
"""Load module using imp.find_module"""
names = name.split(".")
path = None
for name in names:
f, path, info = imp.find_module(name, path)
path = [path]
return imp.load_module(name, f, path[0], info)
constants = load_module("app.constants")
আবার এটি স্থায়ী সমাধান নয় তবে এমন কাউকে সহায়তা করতে পারে যে কোডের খুব বেশি পরিবর্তন না করেই আমদানি ত্রুটিটি ঠিক করতে চায়।
চিয়ার্স!
এখানে দুর্দান্ত উত্তর রয়েছে are সমস্যাটির দ্রুত সমাধানগুলির মধ্যে সাধারণত কিছু রয়েছে যাঁর মধ্যে কেউ কেউ অন্যের চেয়ে বেশি পাইথোনিক বোধ করেন, যদি আপনার কিছু রিফ্যাক্টরিং করার বিলাসিতা থাকে তবে অন্য পদ্ধতির মাধ্যমে আপনার কোডের সংগঠনটি বিশ্লেষণ করা হয় এবং বৃত্তাকার নির্ভরতা অপসারণ করার চেষ্টা করা হয়। উদাহরণস্বরূপ, আপনি এটি পেতে পারেন:
ফাইল a.py
from b import B
class A:
@staticmethod
def save_result(result):
print('save the result')
@staticmethod
def do_something_a_ish(param):
A.save_result(A.use_param_like_a_would(param))
@staticmethod
def do_something_related_to_b(param):
B.do_something_b_ish(param)
ফাইল বি.পি.
from a import A
class B:
@staticmethod
def do_something_b_ish(param):
A.save_result(B.use_param_like_b_would(param))
এই ক্ষেত্রে, কেবল একটি স্থিতিশীল পদ্ধতিকে একটি পৃথক ফাইলে সরানো, বলুন c.py
:
ফাইল সি.পি.
def save_result(result):
print('save the result')
এ save_result
থেকে পদ্ধতিটি সরিয়ে ফেলতে অনুমতি দেবে এবং এভাবে খ-এ থেকে এ এর আমদানি সরিয়ে দেবে:
রিফ্যাক্টর ফাইল এপি
from b import B
from c import save_result
class A:
@staticmethod
def do_something_a_ish(param):
A.save_result(A.use_param_like_a_would(param))
@staticmethod
def do_something_related_to_b(param):
B.do_something_b_ish(param)
রিফ্যাক্টর ফাইল বি.পি.
from c import save_result
class B:
@staticmethod
def do_something_b_ish(param):
save_result(B.use_param_like_b_would(param))
সংক্ষেপে, যদি আপনার কাছে একটি সরঞ্জাম থাকে (যেমন পাইলট বা পাইচার্ম) যা স্থির হতে পারে এমন পদ্ধতিগুলির প্রতিবেদন করে, কেবলমাত্র staticmethod
তাদের উপর একটি ডিকোরেটর নিক্ষেপ করা সতর্কতাটি নিঃশব্দ করার সর্বোত্তম উপায় নাও হতে পারে। যদিও পদ্ধতিটি ক্লাসের সাথে সম্পর্কিত বলে মনে হচ্ছে তবুও এটি আলাদা করা ভাল হতে পারে, বিশেষত যদি আপনার বেশ কয়েকটি ঘনিষ্ঠভাবে সম্পর্কিত মডিউল থাকে যা একই কার্যকারিতাটির প্রয়োজন হতে পারে এবং আপনি DRY নীতিগুলি অনুশীলন করতে চান।
বিজ্ঞপ্তি আমদানি বিভ্রান্ত হতে পারে কারণ আমদানি দুটি কাজ করে:
পূর্ববর্তীটি একবারে সম্পন্ন করা হয়, তবে প্রতিটি আমদানির বিবৃতিতে পরবর্তীটি। সার্কুলার আমদানি পরিস্থিতি তৈরি করে যখন আমদানি করা মডিউল আংশিক সম্পাদিত কোড সহ আমদানিকৃত ব্যবহার করে। ফলস্বরূপ এটি আমদানির বিবৃতি দেওয়ার পরে তৈরি বস্তুগুলি দেখতে পাবে না। কোডের নমুনার নীচে এটি দেখায়।
সার্কুলার আমদানিগুলি সর্বদাই এড়ানো চূড়ান্ত মন্দ নয়। ফ্লাস্কের মতো কিছু ফ্রেমওয়ার্কগুলিতে এগুলি বেশ প্রাকৃতিক এবং আপনার কোডগুলি মুছে ফেলার জন্য তাদের কোডকে কোডিং আরও ভাল করে না।
main.py
print 'import b'
import b
print 'a in globals() {}'.format('a' in globals())
print 'import a'
import a
print 'a in globals() {}'.format('a' in globals())
if __name__ == '__main__':
print 'imports done'
print 'b has y {}, a is b.a {}'.format(hasattr(b, 'y'), a is b.a)
b.by
print "b in, __name__ = {}".format(__name__)
x = 3
print 'b imports a'
import a
y = 5
print "b out"
a.py
print 'a in, __name__ = {}'.format(__name__)
print 'a imports b'
import b
print 'b has x {}'.format(hasattr(b, 'x'))
print 'b has y {}'.format(hasattr(b, 'y'))
print "a out"
মন্তব্য সহ পাইথন main.py আউটপুট
import b
b in, __name__ = b # b code execution started
b imports a
a in, __name__ = a # a code execution started
a imports b # b code execution is already in progress
b has x True
b has y False # b defines y after a import,
a out
b out
a in globals() False # import only adds a to main global symbol table
import a
a in globals() True
imports done
b has y True, a is b.a True # all b objects are available
আমি নিম্নলিখিত উপায়ে সমস্যার সমাধান করেছি এবং এটি কোনও ত্রুটি ছাড়াই ভাল কাজ করে। দুটি ফাইল বিবেচনা করুন a.py
এবং b.py
।
আমি a.py
এটিতে যুক্ত করেছি এবং এটি কাজ করে।
if __name__ == "__main__":
main ()
import b
y = 2
def main():
print ("a out")
print (b.x)
if __name__ == "__main__":
main ()
import a
print ("b out")
x = 3 + a.y
আউটপুট আমি পাই
>>> b out
>>> a out
>>> 5
ঠিক আছে, আমি মনে করি আমার একটি দুর্দান্ত সমাধান আছে। ধরা যাক আপনার ফাইল a
এবং ফাইল রয়েছে b
। আপনি একটি আছে def
বা class
ফাইলে b
যে আপনার মডিউলে ব্যবহার করতে চান a
, কিন্তু আপনি অন্য কিছু আছে, হয় একটি def
, class
অথবা ফাইল থেকে পরিবর্তনশীল a
যে আপনি আপনার সংজ্ঞা বা ফাইলে ক্লাসে প্রয়োজন b
। আপনি যা করতে পারেন তা হ'ল ফাইলের নীচে, a
ফাংশন বা ফাইলটি ক্লাস করার পরে যা ফাইলের a
জন্য প্রয়োজন b
, তবে ফাংশন বা ক্লাসটি ফাইলের আগে কল করার আগে b
আপনার ফাইলটি প্রয়োজনীয় a
, import b
তারপরে বলুন , এবং এখানে মূল অংশটি রয়েছে , সংজ্ঞা বা ফাইল ক্লাস সব b
প্রয়োজন যে def
বা class
ফাইল থেকেa
(আসুন এটি কল করুন CLASS
), আপনি বলেfrom a import CLASS
এটি কাজ করে কারণ আপনি b
পাইথন কোনও ফাইলের আমদানির বিবৃতি কার্যকর না করেই ফাইলটি আমদানি করতে পারেন b
এবং এভাবে আপনি কোনও বৃত্তাকার আমদানি বাদ দেন ।
উদাহরণ স্বরূপ:
class A(object):
def __init__(self, name):
self.name = name
CLASS = A("me")
import b
go = B(6)
go.dostuff
class B(object):
def __init__(self, number):
self.number = number
def dostuff(self):
from a import CLASS
print "Hello " + CLASS.name + ", " + str(number) + " is an interesting number."
Voila।
from a import CLASS
আসলে এপিপিতে সমস্ত কোড কার্যকর করা এড়িয়ে যায় না সত্যই এটি ঘটে: (1) a.py এর সমস্ত কোড একটি বিশেষ মডিউল "__main__" হিসাবে চালিত হয়। (২) এখন import b
, b.py এ শীর্ষ স্তরের কোডটি রান হয়ে যায় (ক্লাস বি সংজ্ঞায়িত) এবং তারপরে "__main__" এ ফিরে আসা নিয়ন্ত্রণ করে। (3) "__main__" অবশেষে নিয়ন্ত্রণে চলে যায় go.dostuff()
। (4) যখন ডাস্টফ () আসে import a
, এটি আবার a.py তে সমস্ত কোড চালায় , এবার মডিউল "এ" হিসাবে; তারপরে এটি নতুন মডিউল "এ" থেকে CLASS অবজেক্টটি আমদানি করে। সুতরাং বাস্তবে, import a
আপনি b.py. এ কোথাও ব্যবহার করলে এটি সমানভাবে কাজ করবে।