[英]Importing files from different folder
我有這個文件夾結構:
application
├── app
│ └── folder
│ └── file.py
└── app2
└── some_folder
└── some_file.py
如何從file.py
中的 file.py 導入some_file.py
? 我試過了:
from application.app.folder.file import func_name
但它不起作用。
注意:此答案旨在解決一個非常具體的問題。 對於大多數從搜索引擎來到這里的程序員來說,這不是您要尋找的答案。 通常,您會將文件構造成包(請參閱其他答案),而不是修改搜索路徑。
默認情況下,您不能。 導入文件時,Python 僅搜索運行入口點腳本的目錄和sys.path
,其中包括包安裝目錄等位置(實際上比這要復雜一些,但這涵蓋了大多數情況)。
但是,您可以在運行時添加到 Python 路徑:
# some_file.py
import sys
# insert at 1, 0 is the script path (or '' in REPL)
sys.path.insert(1, '/path/to/application/app/folder')
import file
沒有錯:
from application.app.folder.file import func_name
只需確保folder
還包含一個__init__.py
,這允許它作為一個包包含。 不知道為什么其他答案談論PYTHONPATH
。
當模塊位於並行位置時,如問題所示:
application/app2/some_folder/some_file.py
application/app2/another_folder/another_file.py
此簡寫使一個模塊對另一個模塊可見:
import sys
sys.path.append('../')
首先在 name-file.py 中導入 sys
import sys
其次在 name-file.py 中附加文件夾路徑
sys.path.insert(0, '/the/folder/path/name-package/')
第三在您的子目錄中創建一個名為 __ init __.py 的空白文件(這告訴 Python 它是一個包)
四、在name-file.py文件夾內導入模塊
from name-package import name-module
我認為一種特別的方法是使用文檔中描述 的環境變量PYTHONPATH
: Python2 , Python3
# Linux & OSX
export PYTHONPATH=$HOME/dirWithScripts/:$PYTHONPATH
# Windows
set PYTHONPATH=C:\path\to\dirWithScripts\;%PYTHONPATH%
您的問題是 Python 正在 Python 目錄中查找此文件,但沒有找到它。 您必須指定您正在談論的是您所在的目錄,而不是 Python 目錄。
為此,您需要更改:
from application.app.folder.file import func_name
對此:
from .application.app.folder.file import func_name
通過添加點,您要在此文件夾中查找應用程序文件夾,而不是在 Python 目錄中查找。
這里的答案不夠清晰,這是在 Python 3.6 上測試的
使用此文件夾結構:
main.py
|
---- myfolder/myfile.py
myfile.py
的內容在哪里:
def myfunc():
print('hello')
main.py
中的導入語句為:
from myfolder.myfile import myfunc
myfunc()
這將打印hello 。
在 Python 3.4 及更高版本中,您可以直接從源文件導入(鏈接到文檔) 。 這不是最簡單的解決方案,但為了完整起見,我將這個答案包括在內。
這是一個例子。 首先,要導入的文件,名為foo.py
:
def announce():
print("Imported!")
導入上述文件的代碼深受文檔中示例的啟發:
import importlib.util
def module_from_file(module_name, file_path):
spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
foo = module_from_file("foo", "/path/to/foo.py")
if __name__ == "__main__":
print(foo)
print(dir(foo))
foo.announce()
輸出:
<module 'foo' from '/path/to/foo.py'>
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'announce']
Imported!
請注意,變量名、模塊名和文件名不必匹配。 此代碼仍然有效:
import importlib.util
def module_from_file(module_name, file_path):
spec = importlib.util.spec_from_file_location(module_name, file_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
baz = module_from_file("bar", "/path/to/foo.py")
if __name__ == "__main__":
print(baz)
print(dir(baz))
baz.announce()
輸出:
<module 'bar' from '/path/to/foo.py'>
['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'announce']
Imported!
以編程方式導入模塊是在 Python 3.1 中引入的,讓您可以更好地控制模塊的導入方式。 有關詳細信息,請參閱文檔。
嘗試 Python 的相對導入:
from ...app.folder.file import func_name
每個前導點都是從當前目錄開始的層次結構中的另一個更高級別。
問題? 如果這對您不起作用,那么您可能會被許多 gotcha 的相對導入所吸引。 閱讀答案和評論以獲取更多詳細信息: 即使使用 __init__.py,如何修復“嘗試在非包中進行相對導入”
提示:在每個目錄級別都有__init__.py
。 您可能需要從頂級目錄運行的python -m application.app2.some_folder.some_file
(不包括 .py),或者在 PYTHONPATH 中有該頂級目錄。 呸!
據我所知,直接在要導入的函數的文件夾中添加一個__init__.py
文件就可以了。
將應用程序移動到其他環境時,將sys.path.append
與絕對路徑一起使用並不理想。 使用相對路徑並不總是有效,因為當前工作目錄取決於腳本的調用方式。
由於應用程序文件夾結構是固定的,我們可以使用os.path
來獲取我們希望導入的模塊的完整路徑。 例如,如果這是結構:
/home/me/application/app2/some_folder/vanilla.py
/home/me/application/app2/another_folder/mango.py
假設您要導入mango模塊。 您可以在vanilla.py中執行以下操作:
import sys, os.path
mango_dir = (os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
+ '/another_folder/')
sys.path.append(mango_dir)
import mango
當然,您不需要mango_dir變量。
要了解其工作原理,請查看此交互式會話示例:
>>> import os
>>> mydir = '/home/me/application/app2/some_folder'
>>> newdir = os.path.abspath(os.path.join(mydir, '..'))
>>> newdir
'/home/me/application/app2'
>>> newdir = os.path.abspath(os.path.join(mydir, '..')) + '/another_folder'
>>>
>>> newdir
'/home/me/application/app2/another_folder'
>>>
並檢查os.path文檔。
另外值得注意的是,使用packages時處理多個文件夾會更容易,因為可以使用帶點的模塊名稱。
我面臨着同樣的挑戰,尤其是在導入多個文件時,這就是我設法克服的方法。
import os, sys
from os.path import dirname, join, abspath
sys.path.insert(0, abspath(join(dirname(__file__), '..')))
from root_folder import file_name
在 Linux 上的 python3 中為我工作
import sys
sys.path.append(pathToFolderContainingScripts)
from scriptName import functionName #scriptName without .py extension
將application
視為 python 項目的根目錄,在application
、 app
和folder
文件夾中創建一個空的__init__.py
文件。 然后在您的some_file.py
中進行如下更改以獲得 func_name 的定義:
import sys
sys.path.insert(0, r'/from/root/directory/application')
from application.app.folder.file import func_name ## You can also use '*' wildcard to import all the functions in file.py file.
func_name()
創建包的最佳實踐可以是從最高級別目錄中的main_module.py
等模塊運行和訪問其他模塊。
該結構展示了您可以通過使用頂級目錄文件main_module.py
來使用和訪問子包、父包或同級包和模塊。
創建並運行這些文件和文件夾以進行測試:
package/
|
|----- __init__.py (Empty file)
|------- main_module.py (Contains: import subpackage_1.module_1)
|------- module_0.py (Contains: print('module_0 at parent directory, is imported'))
|
|
|------- subpackage_1/
| |
| |----- __init__.py (Empty file)
| |----- module_1.py (Contains: print('importing other modules from module_1...')
| | import module_0
| | import subpackage_2.module_2
| | import subpackage_1.sub_subpackage_3.module_3)
| |----- photo.png
| |
| |
| |----- sub_subpackage_3/
| |
| |----- __init__.py (Empty file)
| |----- module_3.py (Contains: print('module_3 at sub directory, is imported'))
|
|------- subpackage_2/
| |
| |----- __init__.py (Empty file)
| |----- module_2.py (Contains: print('module_2 at same level directory, is imported'))
現在運行main_module.py
輸出是
>>>'importing other modules from module_1...'
'module_0 at parent directory, is imported'
'module_2 at same level directory, is imported'
'module_3 at sub directory, is imported'
打開圖片和文件注意:
在包結構中,如果要訪問照片,請使用最高級別目錄中的絕對目錄。
假設您正在運行main_module.py
並且您想在module_1.py
photo.png
module_1.py
必須包含的是:
正確的:
image_path = 'subpackage_1/photo.png'
cv2.imread(image_path)
錯誤的:
image_path = 'photo.png'
cv2.imread(image_path)
盡管module_1.py
和photo.png
位於同一目錄中。
├───root
│ ├───dir_a
│ │ ├───file_a.py
│ │ └───file_xx.py
│ ├───dir_b
│ │ ├───file_b.py
│ │ └───file_yy.py
│ ├───dir_c
│ └───dir_n
您可以將父目錄添加到PYTHONPATH
,為了實現這一點,您可以在sys.path
中列出的“模塊搜索路徑”中使用取決於操作系統的路徑。 因此,您可以輕松添加父目錄,如下所示:
# file_b.py
import sys
sys.path.insert(0, '..')
from dir_a.file_a import func_name
這在 Windows 上對我有用
# some_file.py on mainApp/app2
import sys
sys.path.insert(0, sys.path[0]+'\\app2')
import some_file
就我而言,我有一個要導入的課程。 我的文件看起來像這樣:
# /opt/path/to/code/log_helper.py
class LogHelper:
# stuff here
在我的主文件中,我通過以下方式包含了代碼:
import sys
sys.path.append("/opt/path/to/code/")
from log_helper import LogHelper
我很特別:我在 Windows 上使用 Python!
我只是完成了信息:對於 Windows 和 Linux,相對路徑和絕對路徑都適用於sys.path
(我需要相對路徑,因為我在幾台 PC 上和不同的主目錄下使用我的腳本)。
在使用 Windows 時, \
和/
都可以用作文件名的分隔符,當然你必須將\
加倍到 Python 字符串中,
一些有效的例子:
sys.path.append('c:\\tools\\mydir')
sys.path.append('..\\mytools')
sys.path.append('c:/tools/mydir')
sys.path.append('../mytools')
(注意:我認為/
比\
更方便,如果它不那么“Windows-native”,因為它與 Linux 兼容並且更易於編寫和復制到 Windows 資源管理器)
我多次碰到同樣的問題,所以我想分享我的解決方案。
以下解決方案適用於在 Python 版本 3.X 中開發您的應用程序的人,因為自 2020 年 1 月 1 日起不支持 Python 2 。
在 python 3 中,由於Implicit Namespace Packages ,您的項目子目錄中不需要__init__.py
。 請參閱Python 3.3+ 中的包不需要init .py
Project
├── main.py
├── .gitignore
|
├── a
| └── file_a.py
|
└── b
└── file_b.py
在file_b.py
中,我想在文件夾 a 下的file_a.py
中導入一個類A
無需像您當前正在開發新項目那樣安裝軟件包
使用try catch
檢查是否有錯誤。 代碼示例:
import sys
try:
# The insertion index should be 1 because index 0 is this file
sys.path.insert(1, '/absolute/path/to/folder/a') # the type of path is string
# because the system path already have the absolute path to folder a
# so it can recognize file_a.py while searching
from file_a import A
except (ModuleNotFoundError, ImportError) as e:
print("{} fileure".format(type(e)))
else:
print("Import succeeded")
一旦你安裝了你的應用程序(在這篇文章中,安裝教程不包括在內)
你可以簡單地
try:
from __future__ import absolute_import
# now it can reach class A of file_a.py in folder a
# by relative import
from ..a.file_a import A
except (ModuleNotFoundError, ImportError) as e:
print("{} fileure".format(type(e)))
else:
print("Import succeeded")
快樂編碼!
如果從特定路徑加載模塊的目的是在開發自定義模塊期間為您提供幫助,您可以在測試腳本的同一文件夾中創建一個指向自定義模塊根目錄的符號鏈接。 對於在該文件夾中運行的任何腳本,此模塊引用將優先於安裝的任何其他同名模塊。
我在 Linux 上對此進行了測試,但它應該適用於任何支持符號鏈接的現代操作系統。
這種方法的一個優點是您可以指向位於您自己的本地 SVC 分支工作副本中的模塊,這可以大大簡化開發周期時間並減少管理模塊不同版本的故障模式。
我正在處理我希望用戶通過pip install a
項目a
並帶有以下文件列表:
.
├── setup.py
├── MANIFEST.in
└── a
├── __init__.py
├── a.py
└── b
├── __init__.py
└── b.py
安裝程序.py
from setuptools import setup
setup (
name='a',
version='0.0.1',
packages=['a'],
package_data={
'a': ['b/*'],
},
)
清單文件
recursive-include b *.*
一個/初始化.py
from __future__ import absolute_import
from a.a import cats
import a.b
a/a.py
cats = 0
a/b/初始化.py
from __future__ import absolute_import
from a.b.b import dogs
a/b/b.py
dogs = 1
我通過使用MANIFEST.in
從目錄運行以下命令來安裝模塊:
python setup.py install
然后,從我的文件系統/moustache/armwrestle
上一個完全不同的位置,我能夠運行:
import a
dir(a)
這證實了a.cats
確實等於 0 而abdogs
確實等於 1,正如預期的那樣。
而不是僅僅做一個import ...
,這樣做:
from <MySubFolder> import <MyFile>
MyFile 位於 MySubFolder 中。
哇,我沒想到會花這么多時間在這上面。 以下對我有用:
操作系統:Windows 10
蟒蛇:v3.10.0
注意:由於我是 Python v3.10.0,我沒有使用__init__.py
文件,這對我來說無論如何都不起作用。
application
├── app
│ └── folder
│ └── file.py
└── app2
└── some_folder
└── some_file.py
WY Hsu 的第一個解決方案對我有用。 為了清楚起見,我已經用絕對文件參考重新發布了它:
import sys
sys.path.insert(1, 'C:\\Users\\<Your Username>\\application')
import app2.some_folder.some_file
some_file.hello_world()
替代解決方案:但是,這也對我有用:
import sys
sys.path.append( '.' )
import app2.some_folder.some_file
some_file.hello_world()
雖然,我不明白為什么它會起作用。 我認為點是對當前目錄的引用。 但是,當打印出當前文件夾的路徑時,當前目錄已經列在頂部:
for path in sys.path:
print(path)
希望有人可以在評論中說明為什么這樣做有效。 不過,我也希望它對某人有所幫助。
下面的代碼以 Python 版本安全的方式導入由其路徑給出的 Python 腳本,無論它位於何處:
def import_module_by_path(path):
name = os.path.splitext(os.path.basename(path))[0]
if sys.version_info[0] == 2:
# Python 2
import imp
return imp.load_source(name, path)
elif sys.version_info[:2] <= (3, 4):
# Python 3, version <= 3.4
from importlib.machinery import SourceFileLoader
return SourceFileLoader(name, path).load_module()
else:
# Python 3, after 3.4
import importlib.util
spec = importlib.util.spec_from_file_location(name, path)
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
我在 psutils 的代碼庫中發現了這一點,位於psutils.test.__init__.py的第 1042 psutils.test.__init__.py
(最近一次提交為 09.10.2020 )。
使用示例:
script = "/home/username/Documents/some_script.py"
some_module = import_module_by_path(script)
print(some_module.foo())
重要警告:該模塊將被視為頂級; 從其中的父包中進行的任何相對導入都將失敗。
以防有人仍在尋找解決方案。 這對我有用。
Python 將包含您啟動的腳本的文件夾添加到 PYTHONPATH,因此如果您運行
python application/app2/some_folder/some_file.py
只有文件夾 application/app2/some_folder 被添加到路徑中(不是您正在執行命令的基本目錄)。 相反,將文件作為模塊運行並在 some_folder 目錄中添加 __init__.py 。
python -m application.app2.some_folder.some_file
這會將基本目錄添加到 python 路徑,然后可以通過非相對導入訪問類。
您可以使用 importlib 將模塊導入到您希望使用如下字符串從文件夾中導入模塊的位置:
import importlib
scriptName = 'Snake'
script = importlib.import_module('Scripts\\.%s' % scriptName)
這個例子有一個 main.py ,它是上面的代碼,然后是一個名為 Scripts 的文件夾,然后您可以通過更改scriptName
變量從該文件夾中調用您需要的任何內容。 然后,您可以使用script
來引用此模塊。 例如,如果我在 Snake 模塊中有一個名為Hello()
的函數,則可以通過以下方式運行此函數:
script.Hello()
我已經在 Python 3.6 中對此進行了測試
我通常會為要導入的模塊創建一個符號鏈接。 符號鏈接確保 Python 解釋器可以在當前目錄中找到模塊(您將另一個模塊導入到的腳本); 稍后當您的工作結束時,您可以刪除符號鏈接。 此外,您應該忽略 .gitignore 中的符號鏈接,這樣您就不會意外地將符號鏈接模塊提交到您的存儲庫。 這種方法甚至可以讓您成功地使用與您正在執行的腳本平行的模塊。
ln -s ~/path/to/original/module/my_module ~/symlink/inside/the/destination/directory/my_module
如果您有多個文件夾和子文件夾,您始終可以從主目錄導入任何類或模塊。
例如:項目的樹形結構
Project
├── main.py
├── .gitignore
|
├── src
├────model
| └── user_model.py
|────controller
└── user_controller.py
現在,如果你想從main.py文件中的 user_model.py 導入“UserModel”類,你可以使用:
from src.model.user_model.py import UserModel
此外,您可以使用同一行在user_controller.py文件中導入相同的類:
from src.model.user_model.py import UserModel
總的來說,您可以參考主項目目錄來導入項目目錄內任何 python 文件中的類和文件。
我在使用 Pycharm 時遇到了同樣的問題。 我有這個項目結構
skylake\
backend\
apps\
example.py
configuration\
settings.py
frontend\
...some_stuff
和 example.py 中from configuration import settings
的代碼引發了導入錯誤
問題是當我打開Pycharm時,它認為skylake是根路徑並運行了這段代碼
sys.path.extend(['D:\\projects\\skylake', 'D:/projects/skylake'])
它解決了我的問題
您可以按 f5 刷新 Python shell,或轉到 Run-> Run Module。 這樣,您不必更改目錄即可從文件中讀取內容。 Python 會自動更改目錄。 但是如果你想在 Python Shell 中處理來自不同目錄的不同文件,那么你可以更改 sys 中的目錄,正如 Cameron之前所說。
所以我剛剛右鍵單擊了我的 IDE,並添加了一個新folder
,我想知道為什么我無法從中導入。 后來我意識到我必須右鍵單擊並創建一個 Python 包,而不是一個經典的文件系統文件夾。 或者像其他答案中提到的那樣,添加一個__init__.py
(這使得 python 將文件系統文件夾視為一個包)的事后方法。 在這里添加這個答案,以防有人走這條路。
我曾多次遇到這些問題。 我經常來這個頁面。 在我的最后一個問題中,我必須從固定目錄運行server
,但每當調試時我想從不同的子目錄運行。
import sys
sys.insert(1, /path)
對我不起作用,因為在不同的模塊中,我必須讀取不同的*.csv文件,這些文件都在同一個目錄中。
最后,我猜對我有用的不是pythonic,而是:
我在要調試的模塊上使用了if __main__
,它從不同於通常的路徑運行。
所以:
# On top of the module, instead of on the bottom
import os
if __name__ == '__main__':
os.chdir('/path/for/the/regularly/run/directory')
如果您只想運行腳本而不是實際導入它,則 exec 命令將完成工作
exec(open('/full/or/relative/path').read())
我為人們提供的解決方案。 誰在包中有所有必要的__init__.py
,但導入仍然不起作用。
import sys
import os
sys.path.insert(0, os.getcwd())
import application.app.folder.file as file
您可以使用pip
的pip install -e.
命令。 您必須在項目目錄的根目錄中創建一個名為setup.py
的文件,其中包含以下內容:
from setuptools import find_packages, setup
setup(
name='src',
packages=find_packages(),
version='0.1.0',
description='my_project',
author='author',
license='MIT',
)
然后輸入pip install -e.
在項目的根目錄中。 這將使所有目錄都以其名稱作為模塊被調用。 例如,如果您的根目錄包含子目錄module1
和module2
,每個子目錄中都有腳本,您將能夠使用以下命令從任何子目錄訪問它們,對於module1
:
import module1.script1 as script1
以防萬一有人仍然需要解決方案並且沒有在上面的答案中找到解決方案。 這對我有用:
我有這個文件夾結構:
a
└── b
├── c1
| └── d
| └── a_script.py
└── c2
└── a_lib.py
我需要將a_lib.py
包含在a_script.py
中。 這就是我解決無法識別c2
的錯誤的方法:
import sys
from pathlib import Path
path_scripts = Path(__file__).resolve().parents[2]
sys.path.append(str(path_scripts))
from c2 import a_lib
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.