将模块内的所有函数和 classes 导入 class python

Import all functions and classes inside a module into a class python

我正在尝试将子文件夹中的所有对象导入 python 3.8 中的 class,并且正在努力寻找这样做的方法。我不想手动导入所有对象,因为要列出的文件太多了:

class Foo:
    from bar import {
        one,
        two,
        three,
        # ...
    }

当我使用星号导入所有函数和 classes(例如 from bar import *)时,出现以下错误:

SyntaxError: import * only allowed at module level

此外,我不想将所有内容都放在子范围内(例如 import bar.package 并将所有文件放在 barpackage 子包中),因为函数在 bar 依赖于在 运行 时被传递 self 并且意味着必须将 self 的所有引用更改为 self.package 我只是创建另一个文件夹class 中的方法,因此没有一个非常长的文件。

所以我想我有三个问题:为什么不允许在 class 中导入所有内容,我该如何解决这个问题,是否有更好的方法将多个方法拆分到不同的文件中?

编辑:我已经看到 this post,这是我目前拥有的,但我不想手动导入所有内容,而是想使用 *

编辑 2:也许将 class 的子方法创建为一个包并将其作为 self 导入(例如 import bar.package as self),但这可能会覆盖默认的 self。例如说我有两个文件 foo.pybar.py:

# foo.py
def print_bar(self):
    print("bar")
# bar.py
class Test:
    import foo as self
    def __init__(self):
        print("hi")
        self.one = 1

    def print_one(self):
        print(self.one)

if __name__ == "__main__":
    test_class = Test()
    test_class.print_one()
    test_class.self.print_bar(test_class)

请注意 bar.py 中 运行 的丑陋调用 test_class.self.print_bar(test_class)。在这种情况下,python 不会自动将 class 作为方法传递。如果我去掉函数中传递的 test_class 参数,它不会 运行 并给出 TypeError。我想不惜一切代价避免将 self 传递给该方法。

使用 getattrsetattrbar 的属性进行 for 循环可以模拟导入所有结果,调用函数的 __get__ 方法将return 相同的函数绑定到实例化实例 (see this answer):

# bar.py
def frog(self):
    print(f"frog: {self}")

def horse(self):
    print(f"horse: {self}")

# foo.py
class Foo:
    def __init__(self):
        import bar
        for object_name in dir(bar):
            if not object_name.startswith("__"):
                bound_method = getattr(bar, object_name).__get__(
                    self, 
                    self.__class__
                )
                setattr(self, object_name, bound_method)

>>> from foo import Foo
>>> f = Foo()
>>> f.frog
<bound method frog of <foo.Foo object at 0x10c961a50>>
>>> f.horse
<bound method horse of <foo.Foo object at 0x10c961a50>>
>>> f.frog()
frog: <foo.Foo object at 0x10c961a50>
>>> f.horse()
horse: <foo.Foo object at 0x10c961a50>
>>>

删除 if 语句以包括额外的、默认的、Python 对象(__package____spec__ 等),如果您有其他对象,则对其进行扩展记住过滤器。

请注意,这将需要 class 在通过它访问这些对象之前实例化。

首先:不要这样做

可能有更好的方法来解决您的具体问题。如果你要为一个class定义大量的相关方法,并且想用模块来组织这些方法,那么给每个模块一个基class,在那些class上定义方法]es,然后使用 sub-classing 将模块中的 classes 合并为一个。见下文。

Why is importing all in a class not allowed

因为 class 语句是一个作用域命名空间,其中 Python、 在编译时 ,需要知道哪些名称被引用为全局变量并区分它们来自当地的名字。这在函数中尤为重要,在函数中,通过了解在编译时使用的名称,本地命名空间得到了高度优化。对于 class 语句,'local' 名称成为 class 属性。 class 语句在涉及动态局部名称时比函数更灵活,但是将任意动态名称列表导入 class 定义从未被视为值得支持的用例。

how can I get around this

您可以检查模块并获取所有与 Python 将导入的名称相同的名称,然后使用 setattr() 或分配给 [=15] 在 class 上动态设置这些名称=] 字典(class 语句是后者实际工作的唯一地方)。 import statement documentation 列出导入的内容:

  • 如果模块定义了 __all__,则它被用作要导入的名称列表
  • 否则将导入所有 public 名称(名称不以 _ 开头的所有全局变量)。

因此,下面的代码将实现与在 Foo 的 class 语句中使用 from bar import * 完全相同的效果:

import bar

def _import_all(module, class):
    namespace = vars(module)
    public = (name for name in namespace if name[:1] != "_")
    for name in getattr(module, "__all__", public):
        setattr(class, name, namespace[name])

class Foo:
    pass

_import_all(bar, Foo)

或者,这肯定更多 'hack-y' 并且取决于内部 Python 实现细节:

import bar

class Foo
    locals().update(
        (n, getattr(bar, n))
        for n in getattr(
            bar, "__all__",
            (n for n in dir(bar) if n[:1] != "_")
        )
    )

is there a better way to split multiple methods up into different files?

是的,使用 classes。将所有这些方法放在单独的模块中,每个放在 class 中,然后只需从每个模块中导入那些 classes 并将其用作基础 class:

import bar, spam

class Foo(bar.Base, spam.Base):
    pass

其中 bar.py 将定义 Base class:

class Base:
    def print_bar(self):
        print("bar")

spam 等。您可以根据需要向这些基础混合 class 中添加和删除方法,以及 import 语句来组合基础 classes不会改变。