Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/286.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python中的魔法导入_Python_Class_Dynamic - Fatal编程技术网

Python中的魔法导入

Python中的魔法导入,python,class,dynamic,Python,Class,Dynamic,编辑:我应该指定我正在使用Python2,但我很想看看如何在2或3中解决这个问题 场景: 我有一个名为shapes的软件包 我在shapes中有一个名为factory的模块,它有一个ShapeClassFactory类。 该类可以被传递一个字符串,它将在远程数据库中查找数据,并使用该数据动态定义一个类,然后返回该类 shapes.py: from .factory import ShapeClassFactory __all__ = ['ShapeClassFactory'] from .fa

编辑:我应该指定我正在使用Python2,但我很想看看如何在2或3中解决这个问题

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

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

shapes.py:

from .factory import ShapeClassFactory
__all__ = ['ShapeClassFactory']
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)
实际上,该软件包可用于各种其他软件包和脚本中,例如:

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)
…其思想是,如果在
形状
中找不到该成员,它将使用
形状关联工厂
尝试生成该成员

困难在于,在被请求之前,可用类基本上是未知的,因此预定义的类名列表没有帮助


如果
ShapeClassFactory
无法构建类,我不介意抛出一个
ImportError
,但这样做可能吗?

您可以在初始化时自动在
shapes
命名空间中构建所有可能的对象,只要没有太多可能的类,并且预先初始化类的成本不会太高。您可以在shapes.py中使用如下代码:

from .factory import ShapeClassFactory
__all__ = ['ShapeClassFactory']
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)

import语句将运行正在导入的模块,然后提取请求的名称。没有办法事先知道会要求什么名字。基本上,如果不做修补
\uuuuuu导入\uuuuuuu
(可能是使用元钩子?)之类的事情,您就无法做到这一点,而且您真的不应该这么做。例如,dir(shapes)做什么?正确的方法是您已经完成的操作。如果可能生成的类数量相当少,您可以在
形状的命名空间中生成它们中的每一个。这也是一种标准且合理的方法。我使用生成类的主要原因之一是,如果在远程数据库中定义了新形状,我希望避免必须更新包,因此定义接受类的列表并不是这种情况的真正解决方案。实际上,您可以让这些类自动更新。假设你有一个查询数据库的函数
get\u shape\u names
,我会写一个答案。这是一个有趣的问题。我最初的反应是,你们已经拥有的工厂设置还不错。当然,对于Python来说,这感觉有点冗长,但是再一次,它仍然只是每个“动态导入”增加一行,所以在实际操作中,这并不可怕。它的好处是实现简单易懂。这些东西总是有价值的,不应该轻易扔掉!这样做的问题是,可能有几十个类可用,但通常在单个模块或脚本中只使用2到4个类。因为数据库查询和类构造本身都有很大的成本,所以我只需要生成已请求的类。您可以将类构造的成本转移到实例化上。换句话说,使用
中定义的实例变量而不是类变量。这意味着您只需要一个数据库查询就可以获得可能的形状列表,所有其他查询都会在实例化过程中发生。我对在实例级别定义所有属性的想法感觉不太好——除此之外,这并不实用(即,我想要一些类方法),感觉这段代码在可读性和可维护性方面都是一个倒退。在处理大量实例时,它也感觉到了潜在的内存过剩。