【问题标题】:Import all functions and classes inside a module into a class python将模块内的所有函数和类导入到类python中
【发布时间】:2020-02-23 07:41:20
【问题描述】:

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

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

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

SyntaxError: import * only allowed at module level

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

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

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

编辑 2:也许将类的子方法创建为包并将其作为 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 不会自动将类作为方法传递。如果我去掉函数中传递的test_class 参数,它不会运行并给出TypeError。我想不惜一切代价避免将self 传递给该方法。

【问题讨论】:

  • 为什么你想这样做?为什么不将这些名称放在一个混合类中并导入该类并将其用作导入模块中的基类?
  • 这并不是不使用类的真正理由;递归下降解析器设计中没有任何内容要求您避免使用类,然后尝试在事后将方法重新组装到一个类中,从而违背正常的 Python 模式。
  • 不过,这与我所说的并不矛盾。然后,您在模块中定义多个类,而不是您希望稍后作为方法附加到类的一堆函数。 :-)
  • 啊...我明白你的意思了。是的,我已经这样做了,而不是“丑陋”的全部导入方式。
  • 在旁注中,您可能有兴趣阅读Guido van Rossum is re-visiting the Python language parser design。不同的解析器设计,但仍然如此。

标签: python python-3.x class module python-import


【解决方案1】:

首先:不要这样做

可能有更好的方法来解决您的特定问题。如果你必须为一个类定义大量相关的方法,并且想用模块来组织这些方法,那么给每个模块一个基类,在这些类上定义方法,然后将模块中的类组合成一个使用子分类。见下文。

为什么不允许在一个类中导入所有内容

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

我该如何解决这个问题

您可以自省模块并获取 Python 将导入的所有相同名称,并使用 setattr() 或通过分配给 locals() 字典在类上动态设置这些名称(类语句是后者实际上的唯一位置作品)。 import statement documentation 列出了导入的内容:

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

因此,以下将实现与在 Foo 的类语句中使用 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] != "_")
        )
    )

有没有更好的方法将多个方法分成不同的文件?

是的,使用类。将所有这些方法放在单独的模块中,每个都放在一个类中,然后简单地从每个模块中导入这些类并将其用作基类:

import bar, spam

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

bar.py 将定义一个 Base 类:

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

spam 等也会如此。您可以根据需要向这些 mixin 基类添加和删除方法,用于组合基类的 import 语句不会改变。

【讨论】:

  • 是的,我意识到你可以只使用继承,但这对我的用例来说并没有什么意义。我这样做的原因是因为我正在编写一个递归下降解析器(意味着这些方法是相互依赖的),其中解析器中的规则没有特定的顺序。也许我可以制作整个 Parser 类并让它从较小的类继承。
  • @MonLiH 这就是我说的你应该做的,是的。通过继承一系列“组件”类来创建您的 Parser 类。
【解决方案2】:

使用getattrsetattrbar 的属性进行for 循环可以模拟全部导入结果,并且调用函数的__get__ 方法将返回绑定到实例化实例的相同函数( 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__ 等),如果您有其他过滤器,请对其进行扩展。

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

【讨论】:

  • 这不是我想要的,因为当我运行我的方法时,该类不会自动将self 作为第一个参数传递。这样做会产生以下错误:TypeError: print_bar() missing 1 required positional argument: 'self'。我不想手动传递self (test_class.print_bar(test_class)),因为这个函数被调用了很多而且看起来很乱。
  • 此外,大多数方法都被类中的其他方法调用,这意味着我每次都必须将 self 作为第一个参数传递(例如在类 self.print_bar(self) 中),从而导致更多的编辑。跨度>
  • 我的错误,我不知何故忽略了你问题的那一部分。我已经更新了我的答案以说明绑定方法
  • 感谢您的回答!这看起来很有希望,虽然有点老套。我会接受这个答案,我在几天内找不到更好的答案。
  • 这比它需要的要复杂得多。您仍然可以在 class 语句之后向类添加属性,为每个实例提供绑定方法的静态副本也无法缩放,现在 OP 无法使用属性。跨度>
猜你喜欢
  • 2013-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-15
相关资源
最近更新 更多