从子目录中的不同文件导入类
这是我正在使用的结构:
directory/
script.py
subdir/
__init__.py
myclass01.py
myclass02.py
我想在 script.py
中导入在 myclass01.py
和 myclass02.py
中定义的类。如果我这样做:
from subdir.myclass01 import *
对于在 myclass01.py
中定义的类,这样是可以正常工作的。但是如果有很多类分散在不同的文件中,比如在 subdir
文件夹里,而我想一次性导入所有这些类,那我就得为每个文件都写一行代码。这肯定有更简单的方法。我试过:
from subdir.* import *
但这样并没有成功。
编辑:以下是文件的内容:
这是 __init__.py
(使用 __all__
,正如 Apalala 建议的那样):
__all__ = ['MyClass01','MyClass02']
这是 myclass01.py
:
class MyClass01:
def printsomething():
print 'hey'
这是 myclass02.py
:
class MyClass02:
def printsomething():
print 'sup'
这是 script.py
:
from subdir import *
MyClass01().printsomething()
MyClass02().printsomething()
这是我尝试运行 script.py
时得到的错误信息:
File "script.py", line 1, in <module>
from subdir import *
AttributeError: 'module' object has no attribute 'MyClass01'
4 个回答
我知道这个问题已经有几个月的时间没有人回答了,但我在寻找同样的东西时找到了这个页面。我对选定的答案不是很满意,所以我写了自己的解决方案,想和大家分享一下。以下是我想到的:
# NOTE: The function name starts with an underscore so it doesn't get deleted by iself
def _load_modules(attr_filter=None):
import os
curdir = os.path.dirname(__file__)
imports = [os.path.splitext(fname)[0] for fname in os.listdir(curdir) if fname.endswith(".py")]
pubattrs = {}
for mod_name in imports:
mod = __import__(mod_name, globals(), locals(), ['*'], -1)
for attr in mod.__dict__:
if not attr.startswith('_') and (not attr_filter or attr_filter(mod_name, attr)):
pubattrs[attr] = getattr(mod, attr)
# Restore the global namespace to it's initial state
for var in globals().copy():
if not var.startswith('_'):
del globals()[var]
# Update the global namespace with the specific items we want
globals().update(pubattrs)
# EXAMPLE: Only load classes that end with "Resource"
_load_modules(attr_filter=lambda mod, attr: True if attr.endswith("Resource") else False)
del _load_modules # Keep the namespace clean
这个方法简单地从包目录下的所有 .py 文件中导入内容,然后只把公共的部分放到全局命名空间里。此外,它还允许你设置过滤条件,如果你只想要某些特定的公共属性。
你最好的选择,虽然可能不是最好的写法,就是把所有东西都导入到这个包的命名空间里:
# this is subdir/__init__.py
from myclass01 import *
from myclass02 import *
from myclass03 import *
然后,在其他模块中,你可以直接从这个包里导入你想要的东西:
from subdir import Class1
虽然这里用的名字和你问题中的目录结构不太一样,但你可以参考我在命名空间和类这个问题中的回答。那里的__init__.py
文件也可以让usepackage.py
脚本这样写(package
对应你问题中的subdir
,Class1
对应myclass01
,等等):
from package import *
print Class1
print Class2
print Class3
修订版(更新):
哦,抱歉,我之前的回答中的代码并不能完全满足你的需求——它只是自动导入了任何包子模块的名字。如果想要它同时导入每个子模块中的指定属性,还需要多写几行代码。这里是修改后的包的__init__.py
文件(在Python 3.4.1中也能用):
def _import_package_files():
""" Dynamically import all the public attributes of the python modules in this
file's directory (the package directory) and return a list of their names.
"""
import os
exports = []
globals_, locals_ = globals(), locals()
package_path = os.path.dirname(__file__)
package_name = os.path.basename(package_path)
for filename in os.listdir(package_path):
modulename, ext = os.path.splitext(filename)
if modulename[0] != '_' and ext in ('.py', '.pyw'):
subpackage = '{}.{}'.format(package_name, modulename) # pkg relative
module = __import__(subpackage, globals_, locals_, [modulename])
modict = module.__dict__
names = (modict['__all__'] if '__all__' in modict else
[name for name in modict if name[0] != '_']) # all public
exports.extend(names)
globals_.update((name, modict[name]) for name in names)
return exports
if __name__ != '__main__':
__all__ = ['__all__'] + _import_package_files() # '__all__' in __all__
另外,你可以把上面的内容放到包目录中的一个单独的.py模块文件里,比如叫_import_package_files.py
,然后在包的__init__.py
中这样使用它:
if __name__ != '__main__':
from ._import_package_files import * # defines __all__
__all__.remove('__all__') # prevent export (optional)
无论你给文件起什么名字,都应该以_
下划线开头,这样它就不会试图递归地import
自己。