【问题标题】:magic imports in PythonPython中的魔术导入
【发布时间】:2018-03-10 16:24:21
【问题描述】:

编辑:我应该指定我(卡住)使用 Python 2 工作,但有兴趣看看如何在 2 或 3 中解决这个问题

场景: 我有一个名为shapes 的包。

我在shapes 中有一个名为factory 的模块,它有一个ShapeClassFactory 类。 这个类可以传递一个字符串,它会在远程数据库中查找数据并使用它来动态定义一个类,然后返回。

shapes.py:

from .factory import ShapeClassFactory
__all__ = ['ShapeClassFactory']

实际上,这个包可以用在各种其他包和脚本中,如下所示:

from shapes import ShapeClassFactory

Circle = ShapeClassFactory("Circle")
Rect = ShapeClassFactory("Rect")

myCircle = Circle(r=5, fill='red')
mySquare = Rect(x=5, y=5, fill=None)

问题: 以上都很好。但是,我希望能够以这样的方式编写 shapes 包:

from shapes import Circle, Rect

myCircle = Circle(r=5, fill='red')
mySquare = Rect(x=5, y=5, fill=None)

...这个想法是,如果在shapes 中找不到该成员,它会使用ShapeClassFactory 来尝试生成它。

困难在于可用类在请求之前基本上是未知的,因此预定义的类名列表将无济于事。

如果 ShapeClassFactory 无法构建类,我不介意抛出 ImportError —— 但这样的事情是否可能?

【问题讨论】:

  • import 语句将运行正在导入的模块,然后拉出请求的名称。没有办法事先知道将请求哪些名称。基本上,如果不进行修补 __import__ 之类的操作(可能使用元挂钩?),您就无法做到这一点,而且您真的不应该这样做。例如, dir(shapes) 做什么?正确的方法是你已经做过的。
  • 如果可能生成的类的数量相当少,您可以在shapes 的命名空间中生成每一个。这也是一种标准且合理的方式。
  • 我使用生成的类的主要原因之一是如果在远程数据库中定义了新的形状,我想避免更新包,因此定义接受的类列表不是确实是这种情况的解决方案。
  • 您实际上可以让这些类自动更新。假设您有一个查询数据库的函数get_shape_names,我会写一个答案。
  • 这是一个有趣的问题。我最初的反应是您已经拥有的工厂设置还不错。当然,对于 Python 来说,这有点冗长,但话又说回来,每个“动态导入”仍然只是多一行,所以实际上,这并不可怕。它的好处是实现简单易懂。那总是值得的,不应该掉以轻心!

标签: python class dynamic


【解决方案1】:

您可以通过在初始化时自动在 shapes 命名空间中构造所有可能的对象来做到这一点,只要没有太多可能的类并且预先初始化类的成本不太高。你会在shapes.py中使用这样的代码:

from .factory import ShapeClassFactory

__all__ = ['ShapeClassFactory']

def get_shape_names():
    """Returns all valid shapes that can be passed in to ShapeClassFactory"""
    return ['Circle', 'Rect']  # your own code should query the database

for name in get_shape_names():
    globals()[name] = ShapeClassFactory(name)
    __all__.append(name)

【讨论】:

  • 这样做的问题是可能有几十个类可用,但通常在单个模块或脚本中只使用 2 到 4 个。因为数据库查询和类构造本身都存在大量成本,所以我只想生成已请求的类。
  • 您也许可以将类构造的成本转移到实例化上。换句话说,使用__init__ 中定义的实例变量而不是类变量。这意味着您只需要一个数据库查询来获取可能的形状列表,并且所有其他查询都在实例化期间发生。
  • 我对在实例级别定义所有属性的想法感觉不太好——除了不实用(即我想要一些类方法)之外,感觉代码既是在可读性和可维护性方面倒退。在处理大量实例时,这也感觉像是潜在的内存过剩。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-11-13
  • 1970-01-01
  • 2018-10-25
  • 1970-01-01
  • 2012-08-11
  • 2022-09-24
  • 2010-11-08
相关资源
最近更新 更多