【问题标题】:Select implementation class in deep hierarchies在深层层次结构中选择实现类
【发布时间】:2019-09-23 13:49:59
【问题描述】:

我正在创建一个 Python 模块,要求该模块的最终用户可以选择一个类的特定实现,该类将被整个模块中的各种其他类使用(最好使用默认实现)。

一个限制是这个类嵌入在深层层次结构中。这是一个说明问题的最小示例:

class X():
    pass

class Y():
    pass

Z = X # or Z = Y

class A():
    def __init__(self):
        self.zoo = Z()

class B():
    def __init__(self):
        self.zoo = Z()

class C(B):
    pass

出于本示例的目的,我使用了全局变量Z 来定义将选择哪个类。这确实不理想,需要用户在导入后monkey patch/更改全局变量。这也可以防止在同一个项目中混合不同的实现。

from my_module import A, B, C, Y, Z

Z=Y

class U():
    def __init__():
        self.foo = C()

解决此问题的首选方法是什么?我想避免将类作为构造函数参数传递,因为选择例如XY 应该链接到类而不是实例。由于 Python 不支持参数化导入,我考虑过装饰器和元类,但无法提出简洁的设计。

【问题讨论】:

  • 任何实现都需要选择你想使用的类。或者你想做一个配置?
  • 这种模式似乎容易出现细微的错误,当加载模块时执行某些代码时会发生细微的错误,默认为Z值,而其他代码将使用修改后的Z值执行。
  • 你可以工厂模式来创建和使用你在运行时需要的类

标签: python python-3.x metaprogramming


【解决方案1】:

看起来您的C 实例正在使用XY,但它是由C 类的用户决定的,他们也应该被允许混合使用两者。在这种情况下,实例应该在创建实例时接收决策,这意味着作为构造函数的参数:

class X():
    pass

class Y():
    pass

class A():
    def __init__(self, x_or_y):
        # you will find a more fitting name in your
        # concrete case than x_or_y ;-)
        self.x_or_y = x_or_y
        self.zoo = x_or_y()

class B():
    def __init__(self, x_or_y):
        self.x_or_y = x_or_y
        self.zoo = x_or_y()

class C(B):
    pass

然后像这样创建一个实例:

from my_module import A, B, C, Y

class U():
    def __init__():
        self.foo = C(Y)

【讨论】:

    【解决方案2】:

    另一种方法是执行wxPython does for their version management 之类的操作。这将涉及将您的代码分成三个部分:类库、实际业务逻辑和选择器模块。

    库.py

    from abc import ABC
    
    class Base(ABC):
        ...
    
    class Impl1(Base):
        ...
    
    class Impl2(Base):
        ...
    

    选择器.py

    from typing import Type
    import library 
    
    impl= library.Impl1
    
    def set_impl(cls: Type[library.Base]):
        global impl
        impl = cls
    

    core.py

    import library
    import selector 
    
    impl = selector.impl
    
    class A():
        def __init__(self):
            self.zoo = impl()
    

    然后在使用你的包的代码中,你只需要确保在导入core.py之前调用set_impl

    import library
    import selector
    selector.set_impl(library.Impl2)
    
    import core
    print(core.impl)  # <class 'library.Impl2'>
    

    这是可行的,因为为 selector 模块创建的模块对象只创建一次,并且所有其他模块都共享它。所以当主模块修改selector时,这些变化对core是可见的

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-05-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-18
      • 1970-01-01
      • 2016-04-22
      • 2016-04-12
      相关资源
      最近更新 更多