रिश्तेदार आयात में शीर्ष स्तर पैकेज त्रुटि से परे


316

ऐसा लगता है कि अजगर 3 में रिश्तेदार आयात के बारे में पहले से ही यहां कुछ सवाल हैं, लेकिन उनमें से कई के बाद भी मुझे अपने मुद्दे का जवाब नहीं मिला। तो यहाँ सवाल है।

मेरे पास नीचे दिखाया गया एक पैकेज है

package/
   __init__.py
   A/
      __init__.py
      foo.py
   test_A/
      __init__.py
      test.py

और मेरे पास टेस्टोस्टेरोन में एक ही लाइन है:

from ..A import foo

अब, मैं फ़ोल्डर में packageहूं, और मैं चलाता हूं

python -m test_A.test

मुझे संदेश मिला

"ValueError: attempted relative import beyond top-level package"

लेकिन अगर मैं मूल फ़ोल्डर में packageहूं, उदाहरण के लिए, मैं चलाता हूं:

cd ..
python -m package.test_A.test

सब कुछ ठीक है।

अब मेरा प्रश्न है: जब मैं फ़ोल्डर में packageहूं, और मैं test_A उप-पैकेज के अंदर मॉड्यूल चलाता test_A.testहूं, तो मेरी समझ के आधार पर, ..Aकेवल एक स्तर तक जाता है, जो अभी भी packageफ़ोल्डर के भीतर है , यह संदेश क्यों कहता है beyond top-level package। वास्तव में क्या कारण है जो इस त्रुटि संदेश का कारण बनता है?


49
उस पोस्ट ने मेरे "शीर्ष स्तर के पैकेज से परे" त्रुटि को स्पष्ट नहीं किया
आश्रय

4
मेरा यहाँ एक विचार है, इसलिए जब test_A.test को मॉड्यूल के रूप में चलाते हैं, '..' test_A से ऊपर जाता है, जो पहले से ही आयात test_A.test का उच्चतम स्तर है, मुझे लगता है कि पैकेज स्तर निर्देशिका स्तर नहीं है, लेकिन कितने हैं स्तर आप पैकेज आयात करते हैं।
आश्रय

2
मैं वादा करता हूं कि आप इस जवाब stackoverflow.com/a/14132912/8682868 को देखने के बाद रिश्तेदार आयात के बारे में सब कुछ समझ जाएंगे ।
pzjzeason

मूल्य देखें : इस मुद्दे के बारे में विस्तृत विवरण के लिए शीर्ष स्तर के पैकेज से परे सापेक्ष आयात का प्रयास किया
नपुजबा

क्या रिश्तेदार आयात करने से बचने का एक तरीका है? जैसे कि ग्रहण में PyDev <PydevProject> / src के भीतर सभी पैकेज देखता है?
Mushu909

जवाबों:


172

संपादित करें: इस प्रश्न के अन्य प्रश्नों में बेहतर / अधिक सुसंगत उत्तर हैं:


यह काम क्यों नहीं करता है? यह इसलिए है क्योंकि अजगर रिकॉर्ड नहीं करता है जहां से एक पैकेज लोड किया गया था। इसलिए जब आप ऐसा करते हैं python -m test_A.test, तो यह मूल रूप से केवल उस ज्ञान को त्यागता है जो test_A.testवास्तव में संग्रहीत होता है package(यानी packageपैकेज नहीं माना जाता है)। प्रयास from ..A import fooजानकारी को एक्सेस करने की कोशिश कर रहा है जिसके पास कोई और अधिक (यानी किसी लोड किए गए स्थान की निर्देशिका) नहीं है। यह वैचारिक रूप from ..os import pathसे एक फ़ाइल में अनुमति देने के समान है math। यह बुरा होगा क्योंकि आप चाहते हैं कि पैकेज अलग हों। यदि उन्हें किसी अन्य पैकेज से कुछ का उपयोग करने की आवश्यकता होती है, तो उन्हें विश्व स्तर पर उन्हें संदर्भित करना चाहिए from os import pathऔर अजगर को उस स्थान पर काम करने देना चाहिए जहां वह है $PATHऔर साथ है $PYTHONPATH

जब आप उपयोग करते हैं python -m package.test_A.test, तो from ..A import fooरिज़ॉल्यूशन का उपयोग करना ठीक होता है क्योंकि यह इस बात पर नज़र रखता है कि packageआप क्या कर रहे हैं और आप एक लोड किए गए स्थान की चाइल्ड डायरेक्टरी को एक्सेस कर रहे हैं।

अजगर मौजूदा कामकाजी निर्देशिका को पैकेज क्यों नहीं मानता? नहीं , लेकिन यह उपयोगी होगा।


2
मैंने एक प्रश्न का बेहतर उत्तर देने के लिए अपने उत्तर को संपादित किया है जो एक ही चीज़ पर निर्भर करता है। केवल वर्कअराउंड है। केवल एक चीज जिसे मैंने वास्तव में देखा है वह वह काम है जो ओपी ने किया है, जो -mध्वज का उपयोग करता है और ऊपर की निर्देशिका से चलता है।
मल्टीहंटर

1
यह ध्यान दिया जाना चाहिए कि यह उत्तर , मल्टीहंटर द्वारा दिए गए लिंक से, sys.pathहैक को शामिल नहीं करता है , लेकिन सेटप्टूल का उपयोग , जो मेरी राय में बहुत अधिक दिलचस्प है।
एंजेलो कार्डेलिकिओ

157
import sys
sys.path.append("..") # Adds higher directory to python modules path.

इसे इस्तेमाल करे। मेरे लिए काम किया।


10
उम्म ... इस काम में कैसे जोर लगा? हर एक परीक्षण फ़ाइल में यह होगा?
जॉर्ज मौअर

यहाँ समस्या यह है कि, जैसे, A/bar.pyमौजूद है और foo.pyआप करते हैं from .bar import X
user1834164

9
मुझे sys.path.append ("..") जोड़ने के बाद .. से "से ..A आयात ..." को निकालना था
जेक OPJ

2
यदि स्क्रिप्ट को निर्देशिका के बाहर से निष्पादित किया जाता है, तो यह काम नहीं करेगा। इसके बजाय, आपको उक्त स्क्रिप्ट के पूर्ण पथ को निर्दिष्ट करने के लिए इस उत्तर को मोड़ना होगा
मनावलन गजपति

यह सबसे अच्छा, कम से कम जटिल विकल्प है
एलेक्स आर

43

मान लें:
यदि आप packageनिर्देशिका में हैं, Aऔर test_Aअलग-अलग पैकेज हैं।

निष्कर्ष:
..Aआयात केवल एक पैकेज के भीतर की अनुमति है।

आगे के नोट:
केवल संकुल के भीतर उपलब्ध सापेक्ष आयात करना उपयोगी है यदि आप बल चाहते हैं कि संकुल को किसी भी मार्ग पर स्थित किया जा सकता है sys.path

संपादित करें:

क्या मैं अकेला हूँ जो सोचता है कि यह पागल है !? दुनिया में वर्तमान कामकाजी निर्देशिका को पैकेज क्यों नहीं माना जाता है? - मल्टीहंटर

वर्तमान कामकाजी निर्देशिका आमतौर पर sys.path में स्थित है। इसलिए, सभी फाइलें आयात करने योग्य हैं। यह पायथन 2 के बाद से व्यवहार है जब पैकेज अभी तक मौजूद नहीं थे। रनिंग डायरेक्टरी बनाने से एक पैकेज मॉड्यूल के आयात को "आयात .A" और "आयात ए" के रूप में अनुमति देगा, जो तब दो अलग-अलग मॉड्यूल होंगे। शायद यह विचार करने के लिए एक असंगति है।


85
क्या मैं अकेला हूँ जो सोचता है कि यह पागल है !? क्यों दुनिया में चल रहे निर्देशिका को पैकेज नहीं माना जाता है?
मल्टीहंटर

13
न केवल वह पागल है, यह अनहेल्दी है ... तो आप परीक्षण कैसे चलाते हैं? स्पष्ट रूप से ओपी पूछ रहा था और मुझे यकीन है कि बहुत से लोग यहाँ भी हैं।
जॉर्ज मौअर

चल निर्देशिका आमतौर पर sys.path में स्थित है। इसलिए, वहाँ सभी फाइलें आयात करने योग्य हैं। यह पायथन 2 के बाद से व्यवहार है जब पैकेज अभी तक मौजूद नहीं थे। - संपादित उत्तर।
उपयोगकर्ता

मैं असंगति का पालन नहीं करता। के व्यवहार python -m package.test_A.testक्या चाहता है ऐसा करने के लिए लगता है, और मेरे तर्क यह है कि कि डिफ़ॉल्ट होना चाहिए। तो, क्या आप मुझे इस असंगति का उदाहरण दे सकते हैं?
मल्टीहंटर

मैं वास्तव में सोच रहा हूं, क्या इसके लिए कोई फीचर अनुरोध है? यह वास्तव में पागल है। सी / सी ++ शैली #includeबहुत उपयोगी होगी!
निकोलस हम्फ्री

29

इनमें से कोई भी समाधान मेरे लिए 3.6 में काम नहीं करता, जैसे कि फ़ोल्डर संरचना:

package1/
    subpackage1/
        module1.py
package2/
    subpackage2/
        module2.py

मेरा लक्ष्य मॉड्यूल 1 से मॉड्यूल 2 में आयात करना था। मेरे लिए आखिरकार क्या काम हुआ, काफी अजीब था:

import sys
sys.path.append(".")

अब तक उल्लिखित दो-डॉट समाधानों के विपरीत एकल डॉट को नोट करें।


संपादित करें: निम्नलिखित ने मेरे लिए इसे स्पष्ट करने में मदद की:

import os
print (os.getcwd())

मेरे मामले में, कार्यशील निर्देशिका (अप्रत्याशित रूप से) परियोजना की जड़ थी।


2
यह स्थानीय रूप से काम कर रहा है, लेकिन एक्वस एक् 2 उदाहरण पर काम नहीं कर रहा है, इसका कोई मतलब है?
thebeancounter

इसने मेरे लिए भी काम किया - मेरे मामले में परियोजना की जड़ें इसी तरह काम कर रही थीं। मैं एक प्रोग्रामिंग एडिटर (
टेक्स्टमैट

@ प्रतिशोध ही! मेरे मैक पर स्थानीय रूप से काम करता है, लेकिन यह ec2 पर काम नहीं करता है, तो मुझे एहसास हुआ कि मैं ec2 पर एक उप-कमांड में कमांड चला रहा था और इसे स्थानीय स्तर पर रूट पर चला रहा था। एक बार जब मैंने इसे ec2 पर रूट से चलाया तो यह काम कर गया।
लोगन यांग

यह भी मेरे लिए बहुत सराहना की काम किया। उस sys विधि से मैं अब केवल ".." की आवश्यकता के बिना पैकेज को कॉल कर सकता
हूं

sys.path.append(".")काम किया है क्योंकि आप इसे मूल निर्देशिका में बुला रहे हैं, ध्यान दें कि .हमेशा उस निर्देशिका का प्रतिनिधित्व करें जहां आप पायथन कमांड को चलाते हैं।
केविनज़ू

13

from package.A import foo

मुझे लगता है कि यह स्पष्ट है

import sys
sys.path.append("..")

4
यह सुनिश्चित करने के लिए अधिक पठनीय है लेकिन अभी भी जरूरत है sys.path.append("..")। अजगर 3.6
MFA

पुराने उत्तरों के समान
nrofis

12

सबसे लोकप्रिय जवाब से पता चलता है अपने, मूल रूप से इसकी वजह से PYTHONPATHया sys.pathभी शामिल .है, लेकिन अपने पैकेज के लिए नहीं अपने पथ। और संबंधित आयात आपकी वर्तमान कार्यशील निर्देशिका के सापेक्ष है, न कि उस फ़ाइल का जहां आयात होता है; अजीब तरह से।

आप पहले अपने रिश्तेदार आयात को निरपेक्ष में बदलकर इसे ठीक कर सकते हैं और फिर इसे इसके साथ शुरू कर सकते हैं:

PYTHONPATH=/path/to/package python -m test_A.test

या जब इस तरह से बुलाया अजगर पथ मजबूर कर रहा है, क्योंकि:

साथ python -m test_A.testआप को क्रियान्वित कर रहे हैं test_A/test.pyके साथ __name__ == '__main__'और__file__ == '/absolute/path/to/test_A/test.py'

इसका मतलब है कि test.pyआप importमुख्य मामले की स्थिति में अपने पूर्ण अर्ध-संरक्षित का उपयोग कर सकते हैं और कुछ एक बार पायथन पथ हेरफेर भी कर सकते हैं:

from os import path

def main():

if __name__ == '__main__':
    import sys
    sys.path.append(path.join(path.dirname(__file__), '..'))
    from A import foo

    exit(main())

8

संपादित करें: 2020-05-08: ऐसा लगता है कि मेरे द्वारा उद्धृत वेबसाइट अब सलाह लिखने वाले व्यक्ति द्वारा नियंत्रित नहीं होती है, इसलिए मैं साइट के लिंक को हटा रहा हूं। मुझे baxx पता करने के लिए धन्यवाद।


यदि किसी के अभी भी पहले से ही प्रदान किए गए महान उत्तरों के बाद थोड़ा संघर्ष कर रहा है, तो मुझे एक वेबसाइट पर सलाह मिली कि अब उपलब्ध नहीं है।

मेरे द्वारा बताई गई साइट से आवश्यक उद्धरण:

"इसे इस तरह से प्रोग्रामेटिक रूप से निर्दिष्ट किया जा सकता है:

आयात sys

sys.path.append ( '..')

बेशक अन्य आयात विवरण से पहले कोड ऊपर लिखा जाना चाहिए

यह स्पष्ट है कि यह इस तरह से होना चाहिए, इस तथ्य के बाद इस पर विचार करना। मैं अपने परीक्षणों में sys.path.append ('..') का उपयोग करने की कोशिश कर रहा था, लेकिन ओपी द्वारा पोस्ट किए गए मुद्दे में भाग गया। अपने अन्य आयातों से पहले आयात और sys.path को जोड़ने से, मैं समस्या को हल करने में सक्षम था।


आपके द्वारा पोस्ट किया गया लिंक मृत है।
baxx

मुझे बताने के लिए धन्यवाद। ऐसा लगता है कि डोमेन नाम अब उसी व्यक्ति द्वारा नियंत्रित नहीं किया गया है। मैंने लिंक हटा दिया है।
मायरपो

5

यदि आपके पास एक __init__.pyऊपरी फ़ोल्डर में है, तो आप import file/path as aliasउस init फ़ाइल के रूप में आयात को इनिशियलाइज़ कर सकते हैं । फिर आप निम्न लिपियों पर इसका उपयोग कर सकते हैं:

import alias

0

मेरी विनम्र राय में, मैं इस सवाल को इस तरह से समझता हूं:

[मामला १] जब आप एक पूर्ण आयात शुरू करते हैं

python -m test_A.test

या

import test_A.test

या

from test_A import test

आप वास्तव में आयात-एंकर को test_Aदूसरे शब्द में सेट कर रहे हैं , शीर्ष-स्तरीय पैकेज है test_A। इसलिए, जब हमारे पास test.py है from ..A import xxx, तो आप एंकर से बच रहे हैं, और पायथन इसकी अनुमति नहीं देता है।

[मामला २] जब आप करते हैं

python -m package.test_A.test

या

from package.test_A import test

आपका लंगर बन जाता है package, इसलिए package/test_A/test.pyऐसा करने from ..A import xxxसे लंगर बच नहीं जाता है (अभी भी packageफ़ोल्डर के अंदर ), और पायथन खुशी से इसे स्वीकार करता है।

संक्षेप में:

  • पूर्ण-आयात वर्तमान एंकर को बदल देता है (= शीर्ष-स्तरीय पैकेज को फिर से परिभाषित करता है);
  • रिलेटिव-इंपोर्ट एंकर को नहीं बदलता है बल्कि उसे सीमित करता है।

इसके अलावा, हम इस समस्या का निरीक्षण करने के लिए पूर्ण-योग्य मॉड्यूल नाम (FQMN) का उपयोग कर सकते हैं ।

प्रत्येक मामले में FQMN की जाँच करें:

  • [CASE2] test.__name__=package.test_A.test
  • [CASE1] test.__name__=test_A.test

तो, CASE2 के लिए, from .. import xxxFQMN = के साथ एक नए मॉड्यूल का परिणाम होगा package.xxx, जो स्वीकार्य है।

CASE1 के लिए, ..भीतर से शुरू नोड (लंगर) के from .. import xxxबाहर कूद जाएगा , और यह पायथन द्वारा अनुमति नहीं है।test_A


2
यह जिस तरह से होना चाहिए उससे अधिक जटिल है। पायथन के ज़ेन के लिए इतना।
AtilioA

0

अजगर 2.x में निश्चित नहीं है लेकिन अजगर 3.6 में, यह मानते हुए कि आप पूरे सूट को चलाने की कोशिश कर रहे हैं, आपको इसका उपयोग करना होगा -t

-t, --top-level-directory निर्देशिका परियोजना की शीर्ष स्तर निर्देशिका (निर्देशिका शुरू करने के लिए चूक)

तो, जैसे एक संरचना पर

project_root
  |
  |----- my_module
  |          \
  |           \_____ my_class.py
  |
  \ tests
      \___ test_my_func.py

एक उदाहरण के लिए उपयोग कर सकते हैं:

python3 unittest discover -s /full_path/project_root/tests -t /full_path/project_root/

और अभी भी my_module.my_classप्रमुख नाटकों के बिना आयात करते हैं ।

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