在 Python 中从相对路径导入

我有一个用于客户端代码的文件夹,一个用于服务器代码的文件夹,以及一个用于它们之间共享的代码的文件夹

Proj/
Client/
Client.py
Server/
Server.py
Common/
__init__.py
Common.py

如何从 Server.py 和 Client.py 导入 Common.py?

230277 次浏览

不要做相对重要性。

来自 PEP8:

对于包装内进口的相对进口是非常不鼓励的。

将所有代码放入一个超级包(即“ myapp”) ,并对客户端、服务器和公共代码使用子包。

更新:Python 2.6和3.x 支持正确的相对导入(...)”。请参阅 Dave 的回答了解更多细节。

默认的导入方法已经是来自 PYTHONPATH 的“相对”。默认情况下,PYTHONPATH 与原始源文件的文件夹一起指向某些系统库。如果你用-m 运行一个模块,工作目录会被添加到 PYTHONPATH。因此,如果程序的入口点位于 Proj 内部,那么使用 import Common.Common应该可以在 Server.py 和 Client.py 内部工作。

不要做相对导入,它不会按照你想要的方式工作。

编辑2014年11月(3年后) :

Python 2.6和3.x 支持正确的相对导入,这样可以避免做任何不正常的事情。使用这种方法,您将获得 亲戚导入,而不是 绝对的导入。那个。.意思是,进入我上面的目录:

from ..Common import Common

需要注意的是,只有在将 python 作为模块从包的 在外面运行时,这种方法才会有效。例如:

python -m Proj

原汁原味的曲棍球

这种方法在某些情况下仍然经常使用,在这些情况下,您实际上并没有“安装”您的软件包。例如,它在 Django 用户中很流行。

您可以将 Common/添加到 sys.path (python 查看以导入内容的路径列表) :

import sys, os
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'Common'))
import Common

os.path.dirname(__file__)只提供当前 python 文件所在的目录,然后我们导航到‘ Common/’目录并导入‘ Common’模块。

做一个相对重要的绝对没问题! 这是我做的小事:

#first change the cwd to the script path
scriptPath = os.path.realpath(os.path.dirname(sys.argv[0]))
os.chdir(scriptPath)


#append the relative location you want to import from
sys.path.append("../common")


#import your module stored in '../common'
import common.py

有趣的是,同样的问题,我刚刚遇到,我得到这个工作的方式:

结合 linux 命令 ln,我们可以使事情简单很多:

1. cd Proj/Client
2. ln -s ../Common ./


3. cd Proj/Server
4. ln -s ../Common ./

现在,如果您想从 file: Proj/Common/Common.py导入 some_stuff到 file: Proj/Client/Client.py,就像这样:

# in Proj/Client/Client.py
from Common.Common import some_stuff

同样适用于 Proj/Server,也适用于 setup.py工艺, 同样的问题在这里讨论 ,希望它有所帮助!

我所使用的方法类似于上面提到的 Gary Beardsley,只是改动很小。

文件名 : Server.py

import os, sys
script_path = os.path.realpath(os.path.dirname(__name__))
os.chdir(script_path)
sys.path.append("..")
# above mentioned steps will make 1 level up module available for import
# here Client, Server and Common all 3 can be imported.


# below mentioned import will be relative to root project
from Common import Common
from Client import Client

对于 Python 世界中的新手来说,这是一个简单的答案

创建一个简单的示例

假设我们在当前的工作目录中运行 ls -R,结果如下:

./second_karma:
enemy.py  import.py  __init__.py  math


./second_karma/math:
fibonacci.py  __init__.py

我们运行这个命令 $ python3 second-karma/import.py

Init . py 是一个空文件,但它应该存在。

现在让我们看看 second-karma/import.py里面有什么:

from .math.fibonacci import Fibonacci
fib = Fibonacci()
print(fib.get_fibonacci(15))

second_karma/math/fibonacci.py里面是什么:

from ..enemy import Enemy
class Fibonacci:
enemy: Enemy


def __init__(self):
self.enemy = Enemy(150,900)
print("Class instantiated")
    

def get_fibonacci(self, which_index: int) -> int:
print(self.enemy.get_hp())
return 4

现在最后一个文件是 second_karma/enemy.py:

class Enemy:
hp: int = 100
attack_low: int = 180
attack_high: int = 360


def __init__(
self,
attack_low: int,
attack_high: int) -> None:
self.attack_low = attack_low
self.attack_high = attack_high


def getAttackPower(
self) -> {"attack_low": int, "attack_high": int}:
return {
"attack_low": self.attack_low,
"attack_high": self.attack_high
}


def get_hp(self) -> int:
return self.hp

现在,一个简单的答案解释为什么它不起作用:

  • Python 有一个包的概念,它基本上是一个包含一个或多个模块的文件夹,以及零个或多个包。
  • 当我们启动 python 时,有两种方法:
    • 向 python 询问 执行特定的模块(python3 path/to/file.py)。
    • 将 python 请求到 执行一个包裹
  • 问题是 import.py提到了导入 .math
    • 在此上下文中,.math意味着“在当前包中查找名为奥数的模块/包”
    • 麻烦:
      • 作为 $ python3 second-karma/import.py执行时,我执行的是一个模块,而不是一个包。因此 python 不知道 .在这个上下文中是什么意思
      • python3 -m second_karma.import
        
      • 现在 import.py是父包 second_karma,因此您的相对导入将工作。

重要提示:

这些 __init__.py是必要的,如果您没有它们,您必须首先创建它们。

Github 中的一个例子