अजगर का गोलाकार आयात?


98

तो मुझे यह त्रुटि हो रही है

Traceback (most recent call last):
  File "/Users/alex/dev/runswift/utils/sim2014/simulator.py", line 3, in <module>
    from world import World
  File "/Users/alex/dev/runswift/utils/sim2014/world.py", line 2, in <module>
    from entities.field import Field
  File "/Users/alex/dev/runswift/utils/sim2014/entities/field.py", line 2, in <module>
    from entities.goal import Goal
  File "/Users/alex/dev/runswift/utils/sim2014/entities/goal.py", line 2, in <module>
    from entities.post import Post
  File "/Users/alex/dev/runswift/utils/sim2014/entities/post.py", line 4, in <module>
    from physics import PostBody
  File "/Users/alex/dev/runswift/utils/sim2014/physics.py", line 21, in <module>
    from entities.post import Post
ImportError: cannot import name Post

और आप देख सकते हैं कि मैं उसी आयात विवरण का उपयोग आगे करता हूं और यह काम करता है? क्या परिपत्र आयात के बारे में कुछ अलिखित नियम है? मैं कॉल स्टैक के नीचे उसी वर्ग का उपयोग कैसे करूं?

जवाबों:


161

मुझे लगता है कि jpmc26 का जवाब, जबकि किसी भी तरह से गलत नहीं है , परिपत्र आयात पर बहुत अधिक नीचे आता है। वे ठीक काम कर सकते हैं, अगर आप उन्हें सही तरीके से सेट करते हैं।

ऐसा करने का सबसे आसान तरीका import my_moduleसिंटेक्स का उपयोग करना है, बजाय from my_module import some_object। पूर्व लगभग हमेशा काम करेगा, भले ही my_moduleहमें वापस आयात करें। उत्तरार्द्ध केवल तभी काम करता है जब my_objectपहले से परिभाषित किया गया होmy_module , जो एक परिपत्र आयात में नहीं हो सकता है।

अपने मामले के लिए विशिष्ट होने के लिए: entities/post.pyकरने के लिए बदलने की कोशिश करें import physicsऔर फिर physics.PostBodyकेवल PostBodyसीधे के बजाय देखें । इसी तरह, केवल करने के बजाय physics.pyकरने के लिए बदलें import entities.postऔर फिर उपयोग करें ।entities.post.PostPost


5
क्या यह जवाब रिश्तेदार आयात के साथ संगत है?
जो

17
क्यों होता है ऐसा?
जुआन पाब्लो सैंटोस

4
यह कहना गलत है कि गैर- fromवाक्यविन्यास हमेशा काम करेगा। यदि मेरे पास class A(object): pass; class C(b.B): passमॉड्यूल और class B(a.A): passमॉड्यूल बी में है तो परिपत्र आयात अभी भी एक समस्या है और यह काम नहीं करेगा।
क्रेजीकस्टा जूल

1
आप सही हैं, मॉड्यूल के शीर्ष स्तर कोड (जैसे आपके उदाहरण में वर्ग घोषणाओं के आधार वर्ग) में कोई भी परिपत्र निर्भरता एक समस्या होगी। यह उस तरह की स्थिति है जहां jpmc का जवाब है कि आपको मॉड्यूल संगठन को रिफलेक्टर करना चाहिए शायद 100% सही है। या तो कक्षा Bको मॉड्यूल में स्थानांतरित करें a, या वर्ग Cको मॉड्यूल में स्थानांतरित करें bताकि आप चक्र को तोड़ सकें। यह भी ध्यान देने योग्य है कि भले ही सर्कल की केवल एक दिशा में शीर्ष-स्तरीय कोड शामिल हो (जैसे कि वर्ग Cमौजूद नहीं था), आपको एक त्रुटि मिल सकती है , जिसके आधार पर मॉड्यूल अन्य कोड द्वारा पहले आयात किया गया था।
ब्लैंकनथ

2
@ टायलर क्रॉम्पटन: मुझे यकीन नहीं है कि आप "मॉड्यूल आयात पूर्ण होना चाहिए" से क्या मतलब है। परिपत्र रिश्तेदार आयात काम कर सकते हैं, जब तक आप मॉड्यूल आयात कर रहे हैं, न कि उनकी सामग्री (जैसे from . import sibling_module, नहीं from .sibling_module import SomeClass)। जब पैकेज की __init__.pyफ़ाइल परिपत्र आयात में शामिल होती है, तो कुछ अधिक सूक्ष्मता होती है , लेकिन समस्या दोनों दुर्लभ है, और शायद importकार्यान्वयन में एक बग है । पायथन बग 23447 देखें , जिसे मैंने एक पैच के लिए प्रस्तुत किया था (जो कि बहुत कम हो गया है)।
ब्लकिंक्थ

51

जब आप पहली बार एक मॉड्यूल (या इसका एक सदस्य) आयात करते हैं, तो मॉड्यूल के अंदर के कोड को किसी अन्य कोड की तरह क्रमिक रूप से निष्पादित किया जाता है; उदाहरण के लिए, यह किसी अलग तरह से व्यवहार नहीं किया जाता है कि एक फ़ंक्शन का शरीर। An importकेवल एक कमांड है जैसे कोई अन्य (असाइनमेंट, एक फंक्शन कॉल def, class)। मान लीजिए कि आपका आयात स्क्रिप्ट के शीर्ष पर होता है, फिर यहां क्या हो रहा है:

  • आप आयात करने का प्रयास करते Worldसे world,world स्क्रिप्ट के निष्पादित हो जाता है।
  • worldस्क्रिप्ट आयात Field, जिसकी वजह सेentities.field स्क्रिप्ट के निष्पादित करने के लिए।
  • यह प्रक्रिया तब तक जारी रहती है जब तक आप नहीं पहुँच जाते entities.post स्क्रिप्ट क्योंकि आपने आयात करने की कोशिश की थीPost
  • entities.postस्क्रिप्ट का कारण बनता हैphysics , क्योंकि यह आयात करने की कोशिश करता मॉड्यूल निष्पादित किया जाना हैPostBody
  • अंत में, physicsआयात करना चाहता है Postसेentities.post
  • मुझे यकीन नहीं है कि क्या entities.postमॉड्यूल अभी तक स्मृति में मौजूद है, लेकिन यह वास्तव में कोई फर्क नहीं पड़ता। या तो मॉड्यूल मेमोरी में नहीं है, या मॉड्यूल के पास अभी तक कोई Postसदस्य नहीं है क्योंकि यह परिभाषित करने के लिए निष्पादित नहीं हुआ हैPost
  • किसी भी तरह, एक त्रुटि तब होती है क्योंकि Postआयात करने के लिए नहीं है

तो नहीं, यह "कॉल स्टैक में आगे काम नहीं कर रहा है"। यह एक स्टैक ट्रेस है जहां त्रुटि हुई, जिसका अर्थ है कि यह Postउस वर्ग में आयात करने की कोशिश कर रहा है । आपको परिपत्र आयात का उपयोग नहीं करना चाहिए। सबसे अच्छे रूप में, इसका नगण्य लाभ है (आमतौर पर, कोई लाभ नहीं ), और यह इस तरह की समस्याओं का कारण बनता है। यह इसे बनाए रखने से बचने के लिए अंडे के गोले पर चलने के लिए मजबूर करने वाले किसी भी डेवलपर पर बोझ डालता है। अपने मॉड्यूल संगठन को रिफलेक्टर करें।


1
होना चाहिए isinstance(userData, Post)। भले ही, आप एक विकल्प नहीं है। परिपत्र आयात काम नहीं करेगा। तथ्य यह है कि आपके पास परिपत्र आयात मेरे लिए एक कोड गंध है। यह सुझाव देता है कि आपके पास कुछ कार्यक्षमता है जिसे तीसरे मॉड्यूल में स्थानांतरित किया जाना चाहिए। मैं यह नहीं कह सकता था कि दोनों पूरी कक्षाओं को देखे बिना क्या होगा।
jpmc26

3
@CpILL थोड़ी देर के बाद, मेरे लिए एक बहुत ही घटिया विकल्प हुआ। यदि आप अभी ऐसा करने के लिए आस-पास नहीं पहुंच सकते हैं (समय की कमी के कारण या आपके पास क्या है), तो आप अपने आयात को स्थानीय रूप से उस विधि के अंदर कर सकते हैं जहां आप इसका उपयोग कर रहे हैं। एक फ़ंक्शन बॉडी को defतब तक निष्पादित नहीं किया जाता है जब तक कि फ़ंक्शन को कॉल नहीं किया जाता है, इसलिए आयात तब तक नहीं होगा जब तक आप वास्तव में फ़ंक्शन को नहीं कहते हैं। तब तक, importएस को काम करना चाहिए क्योंकि मॉड्यूल में से एक कॉल से पहले पूरी तरह से आयात किया गया होगा। यह एक बिल्कुल घृणित हैक है, और यह किसी भी महत्वपूर्ण लंबाई के लिए आपके कोड आधार में नहीं रहना चाहिए।
jpmc26

15
मुझे लगता है कि परिपत्र आयात पर आपका जवाब बहुत कठिन है। सर्कुलर इंपोर्ट आम तौर पर काम करता है अगर आप इसके import fooबजाय सिर्फ करते हैं from foo import Bar। ऐसा इसलिए है क्योंकि अधिकांश मॉड्यूल केवल सामान (जैसे फ़ंक्शन और कक्षाएं) को परिभाषित करते हैं जो बाद में चलता है। मॉड्यूल जो महत्वपूर्ण चीजें करते हैं जब आप उन्हें आयात करते हैं (जैसे एक स्क्रिप्ट द्वारा संरक्षित नहीं if __name__ == "__main__") तो अभी भी परेशानी हो सकती है, लेकिन यह बहुत आम नहीं है।
ब्लेककनथ

6
@ ब्लेकन्थ मुझे लगता है कि आप अजीब समस्याओं पर समय बिताने के लिए खुद को स्थापित कर रहे हैं कि अन्य लोगों को जांच करनी होगी और यदि आप परिपत्र आयात का उपयोग करते हैं तो भ्रमित हो सकते हैं। वे आपको उन पर यात्रा न करने के लिए सावधान रहने के लिए समय बिताने के लिए मजबूर करते हैं, और इसके शीर्ष पर एक कोड गंध होता है जिसे आपके डिजाइन को फिर से तैयार करने की आवश्यकता होती है। मैं इस बारे में गलत हो सकता हूं कि क्या वे तकनीकी रूप से व्यवहार्य हैं, लेकिन वे एक भयानक डिजाइन विकल्प हैं जो जल्द या बाद में समस्या पैदा करते हैं। स्पष्टता और सरलता प्रोग्रामिंग में पवित्र कब्र हैं, और मेरी पुस्तक में परिपत्र आयात दोनों का उल्लंघन करते हैं।
jpmc26

6
वैकल्पिक रूप से, आपने अपनी कार्यक्षमता को बहुत अधिक विभाजित कर दिया है और यही परिपत्र आयात का कारण है। यदि आपके पास दो चीजें हैं जो हर समय एक-दूसरे पर भरोसा करती हैं ; यह सिर्फ उन्हें एक फ़ाइल में रखने के लिए सबसे अच्छा हो सकता है। अजगर जावा नहीं है; अजीब आयात तर्क को रोकने के लिए एकल फ़ाइल में समूह कार्यक्षमता / कक्षाएं नहीं करने का कोई कारण नहीं। :-)
मार्क रिबाऊ

40

परिपत्र निर्भरता को समझने के लिए, आपको याद रखना चाहिए कि पायथन अनिवार्य रूप से एक स्क्रिप्टिंग भाषा है। संकलित समय पर बाहर के तरीकों का निष्पादन होता है। इम्पोर्ट स्टेटमेंट को मेथड कॉल की तरह ही निष्पादित किया जाता है, और उन्हें समझने के लिए आपको उनके बारे में सोचना चाहिए जैसे मेथड कॉल।

जब आप एक आयात करते हैं, तो क्या होता है यह निर्भर करता है कि आप जो फ़ाइल आयात कर रहे हैं वह मॉड्यूल तालिका में पहले से मौजूद है। यदि ऐसा होता है, पायथन जो कुछ भी वर्तमान में प्रतीक तालिका में है का उपयोग करता है। यदि नहीं, तो पायथन मॉड्यूल फ़ाइल को पढ़ना शुरू कर देता है, जो कुछ भी वहां मिलता है उसे संकलित / निष्पादित / आयात करता है। संकलित समय पर संदर्भित प्रतीक पाए जाते हैं या नहीं, यह इस बात पर निर्भर करता है कि वे देखे गए हैं, या अभी तक संकलक द्वारा देखे जा सकते हैं।

कल्पना कीजिए कि आपके पास दो स्रोत फाइलें हैं:

फ़ाइल X.py

def X1:
    return "x1"

from Y import Y2

def X2:
    return "x2"

फ़ाइल Y.py

def Y1:
    return "y1"

from X import X1

def Y2:
    return "y2"

अब मान लीजिए कि आप फाइल को X.py। संकलक X 1 विधि को परिभाषित करके शुरू होता है, और फिर X.py में आयात कथन को हिट करता है। इसके कारण कंपाइलर X.py का संकलन रोक देता है और Y.py संकलित करना शुरू कर देता है। इसके तुरंत बाद कंपाइलर Y.py में इंपोर्ट स्टेटमेंट को हिट करता है। चूंकि X.py पहले से ही मॉड्यूल तालिका में है, पाइथन अनुरोध किए गए किसी भी संदर्भ को संतुष्ट करने के लिए मौजूदा अपूर्ण X.py प्रतीक तालिका का उपयोग करता है। X.py में इंपोर्ट स्टेटमेंट से पहले दिखाई देने वाला कोई भी सिंबल अब सिंबल टेबल में है, लेकिन उसके बाद का कोई सिंबल नहीं है। चूंकि X1 अब आयात स्टेटमेंट से पहले दिखाई देता है, इसलिए इसे सफलतापूर्वक आयात किया जाता है। पाइथन फिर Y.py का संकलन शुरू करता है। ऐसा करने में यह Y2 को परिभाषित करता है और Y.py को पूरा करता है। इसके बाद X.py का संकलन फिर से शुरू होता है, और Y2 को Y.py प्रतीक तालिका में पाता है। संकलन अंततः w / o त्रुटि पूर्ण करता है।

यदि आप कमांड लाइन से Y.py संकलित करने का प्रयास करते हैं तो कुछ बहुत अलग होता है। Y.py को संकलित करते समय, कंपाइलर Y2 को परिभाषित करने से पहले आयात स्टेटमेंट को हिट करता है। फिर यह X.py का संकलन शुरू करता है। जल्द ही यह X.py में आयात कथन को हिट करता है जिसके लिए Y2 की आवश्यकता होती है। लेकिन Y2 अपरिभाषित है, इसलिए संकलन विफल रहता है।

कृपया ध्यान दें कि यदि आप Y1 को आयात करने के लिए X.py संशोधित करते हैं, तो संकलन हमेशा सफल होगा, चाहे आप जो भी फ़ाइल संकलित करें। हालाँकि यदि आपने प्रतीक X2 को आयात करने के लिए फ़ाइल Y.py संशोधित किया है, तो न तो फ़ाइल संकलित होगी।

किसी भी समय जब मॉड्यूल एक्स, या एक्स द्वारा आयातित कोई मॉड्यूल वर्तमान मॉड्यूल को आयात कर सकता है, तो उपयोग न करें:

from X import Y

किसी भी समय आपको लगता है कि एक परिपत्र आयात हो सकता है आपको अन्य मॉड्यूल में चर के संकलन समय संदर्भों से भी बचना चाहिए। निर्दोष दिखने वाले कोड पर विचार करें:

import X
z = X.Y

मान लें कि इस मॉड्यूल के आयात से पहले मॉड्यूल X इस मॉड्यूल को आयात करता है। इसके अलावा मान लीजिए कि Y आयात आयात विवरण के बाद X में परिभाषित किया गया है। तब वाई को परिभाषित नहीं किया जाएगा जब यह मॉड्यूल आयात किया जाता है, और आपको एक संकलन त्रुटि मिलेगी। यदि यह मॉड्यूल पहले Y आयात करता है, तो आप इसके साथ भाग सकते हैं। लेकिन जब आपका कोई सहकर्मी निर्दोष रूप से परिभाषाओं के क्रम को तीसरे मॉड्यूल में बदलता है, तो कोड टूट जाएगा।

कुछ मामलों में आप अन्य मॉड्यूल द्वारा आवश्यक प्रतीक परिभाषाओं के नीचे एक आयात विवरण को स्थानांतरित करके परिपत्र निर्भरता को हल कर सकते हैं। उपरोक्त उदाहरणों में, आयात कथन से पहले की परिभाषाएँ कभी विफल नहीं होती हैं। संकलन के आदेश के आधार पर आयात विवरण के बाद परिभाषाएँ कभी-कभी विफल हो जाती हैं। आप किसी फ़ाइल के अंत में आयात स्टेटमेंट भी डाल सकते हैं, इसलिए जब तक संकलन समय पर किसी भी आयातित प्रतीकों की आवश्यकता नहीं होती है।

ध्यान दें कि मॉड्यूल में आयात स्टेटमेंट नीचे जाने से आप जो कर रहे हैं वह अस्पष्ट हो जाता है। इसके लिए अपने मॉड्यूल के शीर्ष पर एक टिप्पणी के साथ इसे कुछ इस तरह से लिखें:

#import X   (actual import moved down to avoid circular dependency)

सामान्य तौर पर यह एक बुरा अभ्यास है, लेकिन कभी-कभी इससे बचना मुश्किल होता है।


2
मुझे नहीं लगता कि अजगर में कंपाइलर या संकलन समय है
pkqxdd

6
अजगर करता है एक संकलक है, और है @pkqxdd संकलित, संकलन बस आमतौर पर उपयोगकर्ता से दूर छिपा हुआ है। यह थोड़ा भ्रमित करने वाला हो सकता है, लेकिन लेखक के लिए यह स्पष्ट रूप से स्पष्ट वर्णन देना मुश्किल होगा कि पाइथन के संदर्भ के बिना क्या चल रहा है, कुछ हद तक अस्पष्ट है, "संकलन समय"।
हांक


मैं अपनी मशीन पर यह कोशिश करने के लिए आगे बढ़ा और एक अलग परिणाम मिला। Ran X.py लेकिन त्रुटि मिली "Y2 'को' Y 'से आयात नहीं किया जा सकता है। हालांकि कोई समस्या नहीं के साथ Ran Y.py मैं Python पर हूं 3.7.5 क्या आप यह समझाने में मदद कर सकते हैं कि यहां क्या समस्या है?
xuefeng हुआंग

18

आप में से जो मेरे जैसे हैं, वे इस मुद्दे पर Django से आते हैं, आपको पता होना चाहिए कि डॉक्स एक समाधान प्रदान करते हैं: https://docs.djangoproject.com/en/1.10/ref/models/fields/#foreignkey

"... किसी अन्य एप्लिकेशन में परिभाषित मॉडल का उल्लेख करने के लिए, आप स्पष्ट रूप से एक मॉडल को पूर्ण एप्लिकेशन लेबल के साथ निर्दिष्ट कर सकते हैं। उदाहरण के लिए, यदि ऊपर दिए गए निर्माता मॉडल को किसी अन्य एप्लिकेशन में उत्पादन कहा जाता है, तो आपको उपयोग करने की आवश्यकता होगी:

class Car(models.Model):
    manufacturer = models.ForeignKey(
        'production.Manufacturer',
        on_delete=models.CASCADE,
)

दो अनुप्रयोगों के बीच परिपत्र आयात निर्भरता को हल करते समय इस तरह का संदर्भ उपयोगी हो सकता है। ... "


6
मुझे पता है कि मैं "धन्यवाद" कहने के लिए टिप्पणी का उपयोग करने वाला नहीं हूं, लेकिन यह मुझे कुछ घंटों के लिए परेशान कर रहा है। धन्यवाद धन्यवाद धन्यवाद!!!
मिकी

मैं @ मायके से सहमत हूं। मैंने कई ब्लॉग और Stackoverflows को PonyORM के साथ इसका उपाय करने के लिए पढ़ा है। जहां अन्य लोग कहते हैं कि यह बुरा अभ्यास है, या आप अपनी कक्षाओं को परिपत्र होने के लिए क्यों कहेंगे, अच्छी तरह से ORM वही होते हैं जहां ऐसा होता है। क्योंकि बहुत सारे उदाहरण एक ही फाइल में सभी मॉडल डालते हैं, और हम उन उदाहरणों का पालन करते हैं, जब हम प्रति फ़ाइल एक मॉडल का उपयोग करते हैं, तो समस्या स्पष्ट नहीं होती है जब पायथन संकलन में विफल रहता है। फिर भी, जवाब इतना आसान है। जैसा कि माइक ने बताया, बहुत-बहुत धन्यवाद।
trash80

4

मैं फ़ंक्शन (केवल) के भीतर मॉड्यूल आयात करने में सक्षम था, जिसे इस मॉड्यूल से ऑब्जेक्ट्स की आवश्यकता होगी:

def my_func():
    import Foo
    foo_instance = Foo()

अजगर का कितना सुरुचिपूर्ण
यारो

2

यदि आप काफी जटिल ऐप में इस समस्या में भाग लेते हैं, तो यह आपके सभी आयातों के लिए बोझिल हो सकता है। PyCharm इसके लिए एक उपसर्ग प्रदान करता है जो स्वचालित रूप से आयातित प्रतीकों के सभी उपयोग को बदल देगा।

यहां छवि विवरण दर्ज करें


0

मैं निम्नलिखित का उपयोग कर रहा था:

from module import Foo

foo_instance = Foo()

लेकिन छुटकारा पाने के लिए circular referenceमैंने निम्नलिखित कार्य किया और इसने काम किया:

import module.foo

foo_instance = foo.Foo()
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.