如何以可维护和可读的方式访问兄弟包? [英] How can I access sibling packages in a maintainable and readable way?
问题描述
我常常遇到一个包需要使用兄弟包的情况。我想澄清一下,我并没有问过Python如何允许你导入兄弟包,这已被多次询问。相反,我的问题是关于编写可维护代码的最佳实践。
I often end up in a situation where one package needs to use a sibling package. I want to clarify that I'm not asking about how Python allows you to import sibling packages, which has been asked many times. Instead, my question is about a best practice for writing maintainable code.
-
假设我们有
tools
package,函数tools.parse_name()
取决于tools.split_name()
。最初,两者都可能存在于同一文件中,一切都很简单:
Let's say we have a
tools
package, and the functiontools.parse_name()
depends ontools.split_name()
. Initially, both might live in the same file where everything is easy:
# tools/__init__.py
from .name import parse_name, split_name
# tools/name.py
def parse_name(name):
splits = split_name(name) # Can access from same file.
return do_something_with_splits(splits)
def split_name(name):
return do_something_with_name(name)
现在,在某些时候我们决定功能增长并将它们分成两个文件:
Now, at some point we decide that the functions have grown and split them into two files:
# tools/__init__.py
from .parse_name import parse_name
from .split_name import split_name
# tools/parse_name.py
import tools
def parse_name(name):
splits = tools.split_name(name) # Won't work because of import order!
return do_something_with_splits(splits)
# tools/split_name.py
def split_name(name):
return do_something_with_name(name)
问题是 parse_name.py
不能只导入工具包它是自身的一部分。至少,这不允许它在 tools / __ init __。py
中使用下面列出的工具。
The problem is that parse_name.py
can't just import the tools package which it is part of itself. At least, this won't allow it to use tools listed below its own line in tools/__init__.py
.
技术解决方案是导入 tools.split_name
而不是工具
:
The technical solution is to import tools.split_name
rather than tools
:
# tools/__init__.py
from .parse_name import parse_name
from .split_name import split_name
# tools/parse_name.py
import tools.split_name as tools_split_name
def parse_name(name):
splits = tools_split_name.split_name(name) # Works but ugly!
return do_something_with_splits(splits)
# tools/split_name.py
def split_name(name):
return do_something_with_name(name)
此解决方案在技术上有效但如果使用的不仅仅是一个兄弟包,很快就会变得混乱。此外,将包 tools
重命名为 utilities
将是一场噩梦,因为现在所有模块别名也应该更改。
This solution technically works but quickly becomes messy if more than just one sibling packages are used. Moreover, renaming the package tools
to utilities
would be a nightmare, since now all the module aliases should change as well.
它希望避免直接导入函数而是导入包,以便在读取代码时清楚显示函数的来源。如何以可读和可维护的方式处理这种情况?
It would like to avoid importing functions directly and instead import packages, so that it is clear where a function came from when reading the code. How can I handle this situation in a readable and maintainable way?
推荐答案
我不是建议在实践中使用它,但只是为了好玩,这是一个解决方案,使用 pkgutil
和检查
:
I'm not proposing this to be actually used in practice, but just for fun, here is a solution using pkgutil
and inspect
:
import inspect
import os
import pkgutil
def import_siblings(filepath):
"""Import and combine names from all sibling packages of a file."""
path = os.path.dirname(os.path.abspath(filepath))
merged = type('MergedModule', (object,), {})
for importer, module, _ in pkgutil.iter_modules([path]):
if module + '.py' == os.path.basename(filepath):
continue
sibling = importer.find_module(module).load_module(module)
for name, member in inspect.getmembers(sibling):
if name.startswith('__'):
continue
if hasattr(merged, name):
message = "Two sibling packages define the same name '{}'."
raise KeyError(message.format(name))
setattr(merged, name, member)
return merged
问题中的示例变为:
# tools/__init__.py
from .parse_name import parse_name
from .split_name import split_name
# tools/parse_name.py
tools = import_siblings(__file__)
def parse_name(name):
splits = tools.split_name(name) # Same usage as if this was an external module.
return do_something_with_splits(splits)
# tools/split_name.py
def split_name(name):
return do_something_with_name(name)
这篇关于如何以可维护和可读的方式访问兄弟包?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!