Python 撤销十年的单例模式和类级配置 概述

Python 撤销十年的单例模式和类级配置 概述,python,class,singleton,factory,metaclass,Python,Class,Singleton,Factory,Metaclass,我需要复制类的整个继承树。简单地深度复制类对象不起作用;一个合适的工厂模式涉及大量的代码更改;我不知道如何使用元类来实现这一点 背景 我使用的软件实现了对专用外部硬件的支持,通过USB连接到主机。许多年前,人们假设一次只能使用一种类型的硬件。因此,硬件对象被用作单例。多年来,辅助类是基于当前活动的硬件类配置的 目前,不可能将此库同时用于两种类型的硬件,因为不能同时为两种硬件配置类对象 近年来,我们通过为每个硬件创建一个python进程来避免这个问题,但这已经变得站不住脚了 下面是一个极其简化的架

我需要复制类的整个继承树。简单地深度复制类对象不起作用;一个合适的工厂模式涉及大量的代码更改;我不知道如何使用元类来实现这一点

背景 我使用的软件实现了对专用外部硬件的支持,通过USB连接到主机。许多年前,人们假设一次只能使用一种类型的硬件。因此,硬件对象被用作单例。多年来,辅助类是基于当前活动的硬件类配置的

目前,不可能将此库同时用于两种类型的硬件,因为不能同时为两种硬件配置类对象

近年来,我们通过为每个硬件创建一个python进程来避免这个问题,但这已经变得站不住脚了

下面是一个极其简化的架构示例:

# ----------
# Hardware classes
class HwBase():
    def customizeComponent(self, compDict):
        compDict['ComponentBase'].hardware = self

class HwA(HwBase):
    def customizeComponent(self, compDict):
        super().customizeComponent(compDict)
        compDict['AnotherComponent'].prop.configure(1,2,3)

class HwB(HwBase):
    def customizeComponent(self, compDict):
        super().customizeComponent(compDict)
        compDict['AnotherComponent'].prop.configure(4,5,6)


# ----------
# Property classes
class SpecialProperty(property):
    def __init__(self, fvalidate):
        self.fvalidate = fvalidate
        # handle fset, fget, etc. here.
        # super().__init__()

# ----------
# Component classes
class ComponentBase():
    hardware = None

    def validateProp(self, val):
        return val < self.maxVal

    prop = SpecialProperty(fvalidate=validateProp)


class SomeComponent():
    """Users directly instantiate and use this compoent via an interactive shell.
    This component does complex operations with the hardware attribute"""

    def validateThing(self, val):
        return isinstance(val, ComponentBase)

    thing = SpecialProperty(fvalidate=validateThing)


class AnotherComponent():
    """Users directly instantiate and use this compoent via an interactive shell
    This component does complex operations with the hardware attribute"""
    maxVal = 15


# ----------
# Initialization



def initialize():
    """ This is only called once perppython instance."""
    #activeCls = HwA
    activeCls = HwB

    allComponents = {
            'ComponentBase': ComponentBase,
            'SomeComponent': SomeComponent,
            'AnotherComponent': AnotherComponent
    }

    hwInstance = activeCls()
    hwInstance.customizeComponent(allComponents)

    return allComponents

components = initialize()

# ----------
# User code goes here
someInstance1 = components['SomeComponent']()
someInstance2 = components['SomeComponent']()

someInstance1.prop = 10
someInstance2.prop = 10
并具有某种组件缓存,可以在初始化()期间处理所有类对象的创建

<>这是我认为最正确的建筑选择。因为类主体被重新执行 在每次工厂调用中,属性的属性都将引用相应的类对象

缺点:巨大的代码占用。我熟悉通过sed或python脚本进行代码库范围的更改,但这将是相当多的

在组件上添加元类 我不知道如何进行这项工作。基于python,在类创建时会发生以下情况(在类定义缩进结束后发生):

  • 解决MRO条目
  • 确定合适的元类
  • 类名称空间已准备好
  • 类主体被执行
  • 将创建类对象
  • 我需要在定义类之后重做这些步骤(比如工厂函数!),但我不确定如何重做步骤4。具体地说,python文档在第3.3.3.5节中指出,类主体是通过exec()内置的“特殊”形式执行的如何使用不同的局部变量/全局变量集重新执行类主体?即使我使用
    inspect
    sheanigans访问类主体的代码,我也不确定是否能够正确复制模块环境

    即使我弄乱了
    \uuuuu prepare\uuuu
    \uuuu new\uuuuuu
    ,我也不知道如何修复类代码块中引入的关于属性实例化的交叉引用

    组件作为元类 元类是类工厂,就像类是对象工厂一样。可以将某个组件和另一个组件声明为元类,然后在initialize()期间使用Hw对象实例化:


    这类似于工厂模式,但也需要进行大量代码更改:许多类代码必须移动到元类
    \uuuu init\uuuu
    我必须在这里花费更多的时间来正确理解您需要的内容,但是如果您的“TL;DR”使用不同的全局/非局部变量执行类主体是底线,工厂方法是一种非常干净和可读的方法,正如您所考虑的那样

    起初,我认为元类在这里不是一个好方法——尽管它可以用来定制您的特殊属性(在我的第一次阅读中,我不知道它们实际上是做什么的,以及它们在最终类之间的区别)。如果作为类工厂的函数可以专门化您的属性,那么它仍然可以工作

    如果您需要的是属性对于
    Hwa
    HwB
    是独立的,就像在Hwa中访问不同于在HwB中访问的列表对象一样,那么元类可以通过在创建子类时自动重新创建任何属性来解决这个问题(这样属性对象本身就不会与super类和整个层次结构共享)。 如果这是我需要的,请留下评论,我可以写一些概念验证代码

    无论如何,可以创建一个元类,在实例化一个子类时,该元类将查看所有
    SpecialProperty
    的层次结构,并为该子类创建这些元类的新实例-这样一个在超类上设置的基值对于子类仍然有效,但是当配置运行时,每个类都将有一个独立的confi(事实证明,不需要元类:我们被

    另一件需要注意的事情是,属性的子类不能简单地用Python的
    copy.copy
    (经过经验测试)复制,因此我们需要一种方法来创建这些属性的可靠副本。我在下面包括一个函数,但它可能需要改进,以便与实际的
    SpecialProperty
    类一起工作

    从副本导入副本
    def复制_属性(道具):
    cls=道具等级__
    新建项目=cls.\uuuu新建项目(cls)
    #初始化Python代码中无法设置的属性,就地:
    属性。uuuu init_uuuuuuuuuuuuuuuuuuuuu(新属性、属性fget、属性fset、属性fdel)
    如果hasattr(prop,“u dict”):#只存在于属性的子类中
    #可能需要调整:可能是因为
    #特殊性质,需要一份深度拷贝。
    #但对于给定的示例属性“fvalidate”,最好使用简单的副本:
    新文件副本=副本(文件副本)
    归还新道具
    #Python3.6引入了在创建子类时调用的“初始化子类”_
    #有了它,逻辑可以插入ComponentBase,而不需要
    #元类。
    类ComponentBase():
    定义初始子类(cls,**kwargs):
    super().\uuuu init\u子类(**kwargs)
    对于目录中的属性名称(cls):
    attr=getattr(cls,attrname)
    如果不是,则为持续(属性、Sp
    
    def ComponentBaseFactory(hw):
        class SomeComponent(cache[hw].ComponentBase):
            pass
    
    SomeComponent = SomeComponentMeta(hw)