管理器/容器class,如何?

Manager / Container class, how to?

我目前正在设计一个需要管理特定硬件设置的软件。

硬件设置如下:

系统 - 系统包含两个相同的设备,并具有相对于整个系统的某些功能。

设备 - 每个设备包含两个相同的子设备,并且具有与两个子设备相关的特定功能。

子设备 - 每个子设备有 4 个可配置实体(通过相同的硬件命令控制 - 因此我不将它们算作子设备)。

我想要实现的目标:

我想通过系统管理器控制所有可配置的实体(实体以串行方式计算),这意味着我可以执行以下操作:

system_instance = system_manager_class(some_params)
system_instance.some_func(0) # configure device_manager[0].sub_device_manager[0].entity[0]
system_instance.some_func(5) # configure device_manager[0].sub_device_manager[1].entity[1]
system_instance.some_func(8) # configure device_manager[1].sub_device_manager[1].entity[0]

我想到了什么:

我正在考虑创建一个抽象 class,它包含所有子设备函数(调用转换函数)并具有 system_manager、device_manager 和 sub_device_manager 继承吧。因此所有 classes 将具有相同的函数名称,我将能够通过系统管理器访问它们。 这些行周围的东西:

class abs_sub_device():
    @staticmethod
    def convert_entity(self):
        sub_manager = None
        sub_entity_num = None
        pass

    def set_entity_to_2(entity_num):
        sub_manager, sub_manager_entity_num = self.convert_entity(entity_num)
        sub_manager.some_func(sub_manager_entity_num)


class system_manager(abs_sub_device):
    def __init__(self):
        self.device_manager_list = [] # Initiliaze device list
        self.device_manager_list.append(device_manager())
        self.device_manager_list.append(device_manager())

    def convert_entity(self, entity_num):
        relevant_device_manager = self.device_manager_list[entity_num // 4]
        relevant_entity         = entity_num % 4
        return relevant_device_manage, relevant_entity

class device_manager(abs_sub_device):
    def __init__(self):
        self.sub_device_manager_list = [] # Initiliaze sub device list
        self.sub_device_manager_list.append(sub_device_manager())
        self.sub_device_manager_list.append(sub_device_manager())        

    def convert_entity(self, entity_num):
        relevant_sub_device_manager = self.sub_device_manager_list[entity_num // 4]
        relevant_entity         = entity_num % 4
        return relevant_sub_device_manager, relevant_entity

class sub_device_manager(abs_sub_device):
    def __init__(self): 
        self.entity_list = [0] * 4

    def set_entity_to_2(self, entity_num):
        self.entity_list[entity_num] = 2

问题:

在我看来,我正在尝试设计的系统确实是通用的,并且必须有一个内置的 python 方法来做到这一点,否则我的整个面向对象的观点都是错误的.

我真的很想知道是否有人有更好的方法。

这是否解决了您的问题?

class EndDevice:
    def __init__(self, entities_num):
        self.entities = list(range(entities_num))

    @property
    def count_entities(self):
        return len(self.entities)

    def get_entity(self, i):
        return str(i)


class Device:
    def __init__(self, sub_devices):
        self.sub_devices = sub_devices

    @property
    def count_entities(self):
        return sum(sd.count_entities for sd in self.sub_devices)

    def get_entity(self, i):
        c = 0
        for index, sd in enumerate(self.sub_devices):
            if c <= i < sd.count_entities + c:
                return str(index) + " " + sd.get_entity(i - c)
            c += sd.count_entities
        raise IndexError(i)


SystemManager = Device # Are the exact same. This also means you can stack that infinite

sub_devices1 = [EndDevice(4) for _ in range(2)]
sub_devices2 = [EndDevice(4) for _ in range(2)]
system_manager = SystemManager([Device(sub_devices1), Device(sub_devices2)])

print(system_manager.get_entity(0))
print(system_manager.get_entity(5))
print(system_manager.get_entity(15))

我想不出比 OOP 更好的方法来做到这一点,但是继承只会为系统管理器提供一组 low-level 函数,因此就像拥有一个设备管理器和一位 sub-device 经理。更好的做法是,有点像 tkinter 小部件,拥有一个系统管理器并初始化所有其他管理器,如树中的 children,所以:

system = SystemManager()
device1 = DeviceManager(system)
subDevice1 = SubDeviceManager(device1)
device2 = DeviceManager(system)
subDevice2 = SubDeviceManager(device2)

#to execute some_func on subDevice1
system.some_func(0, 0, *someParams)

我们可以通过保留 higher-level 经理的 'children' 列表并具有引用 children.

的函数来做到这一点
class SystemManager:
  def __init__(self):
    self.children = []
  def some_func(self, child, *params):
    self.children[child].some_func(*params)

class DeviceManager:
  def __init__(self, parent):
    parent.children.append(self)
    self.children = []
  def some_func(self, child, *params):
    self.children[child].some_func(*params)

class SubDeviceManager:
  def __init__(self, parent):
    parent.children.append(self)
    #this may or may not have sub-objects, if it does we need to make it its own children list.
  def some_func(self, *params):
    #do some important stuff

不幸的是,这确实意味着如果我们想从系统管理器中调用一个 sub-device 管理器的函数而没有很多点,我们将不得不在系统管理器中再次定义它。你可以做的是使用 built-in exec() 函数,它将接收一个字符串输入并 运行 它使用 Python 解释器:

class SystemManager:
  ...
  def execute(self, child, function, *args):
    exec("self.children[child]."+function+"(*args)")

(并保持设备管理器不变)

然后在主程序中写:

system.execute(0, "some_func", 0, *someArgs)

哪个会调用

device1.some_func(0, someArgs)

这是我的想法:

SystemManager().apply_to_entity(entity_num=7, lambda e: e.value = 2)

class EntitySuperManagerMixin():
    """Mixin to handle logic for managing entity managers."""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)  # Supports any kind of __init__ call.
        self._entity_manager_list = []

    def apply_to_entity(self, entity_num, action):
        relevant_entity_manager = self._entity_manager_list[index // 4]
        relevant_entity_num = index % 4
        return relevant_entity_manager.apply_to_entity(
            relevant_entity_num, action)


class SystemManager(EntitySuperManagerMixin):

    def __init__(self):
        super().__init__()
        # An alias for _entity_manager_list to improve readability.
        self.device_manager_list = self._entity_manager_list
        self.device_manager_list.extend(DeviceManager() for _ in range(4))


class DeviceManager(EntitySuperManagerMixin):

    def __init__(self):
        super().__init__()
        # An alias for _entity_manager_list to improve readability.
        self.sub_device_manager_list = self._entity_manager_list
        self.sub_device_manager_list.extend(SubDeviceManager() for _ in range(4))


class SubDeviceManager():
    """Manages entities, not entity managers, thus doesn't inherit the mixin."""

    def __init__(self):
        # Entities need to be classes for this idea to work.
        self._entity_list = [Entity() for _ in range(4)]

    def apply_to_entity(self, entity_num, action):
        return action(self._entity_list[entity_num])


class Entity():
    def __init__(self, initial_value=0):
        self.value = initial_value

采用这种结构:

  • 实体特定函数可以保持绑定到实体 class(它所属的位置)。
  • 管理器特定的代码需要在两个地方更新:EntitySuperManagerMixin 和最低级别的管理器(无论如何都需要自定义行为,因为它处理的是实际实体,而不是其他管理器)。

经过深思熟虑,我想我找到了一个非常通用的方法来解决这个问题,结合使用装饰器、继承和动态函数创建。

主要思想如下:

1) 每个层都为它自己动态创建所有子层相关函数(在 init 函数内部,在 init 函数上使用装饰器)

2)创建的每个函数根据一个convert函数(是abs_container_class的静态函数)动态转换实体值,并调用lower层的同名函数(见make_convert_function_method).

3) 这基本上导致所有子层功能在更高层次上实现,零代码重复。

def get_relevant_class_method_list(class_instance):
    method_list = [func for func in dir(class_instance) if callable(getattr(class_instance, func)) and not func.startswith("__") and not func.startswith("_")]
    return method_list

def make_convert_function_method(name):
    def _method(self, entity_num, *args):
        sub_manager, sub_manager_entity_num = self._convert_entity(entity_num)
        function_to_call = getattr(sub_manager, name)
        function_to_call(sub_manager_entity_num, *args)        
    return _method


def container_class_init_decorator(function_object):
    def new_init_function(self, *args):
        # Call the init function :
        function_object(self, *args)
        # Get all relevant methods (Of one sub class is enough)
        method_list = get_relevant_class_method_list(self.container_list[0])
        # Dynamically create all sub layer functions :
        for method_name in method_list:
            _method = make_convert_function_method(method_name)
            setattr(type(self), method_name, _method)

    return new_init_function


class abs_container_class():
    @staticmethod
    def _convert_entity(self):
        sub_manager = None
        sub_entity_num = None
        pass

class system_manager(abs_container_class):
    @container_class_init_decorator
    def __init__(self):
        self.device_manager_list = [] # Initiliaze device list
        self.device_manager_list.append(device_manager())
        self.device_manager_list.append(device_manager())
        self.container_list = self.device_manager_list

    def _convert_entity(self, entity_num):
        relevant_device_manager = self.device_manager_list[entity_num // 4]
        relevant_entity         = entity_num % 4
        return relevant_device_manager, relevant_entity

class device_manager(abs_container_class):
    @container_class_init_decorator
    def __init__(self):
        self.sub_device_manager_list = [] # Initiliaze sub device list
        self.sub_device_manager_list.append(sub_device_manager())
        self.sub_device_manager_list.append(sub_device_manager())    
        self.container_list = self.sub_device_manager_list

    def _convert_entity(self, entity_num):
        relevant_sub_device_manager = self.sub_device_manager_list[entity_num // 4]
        relevant_entity         = entity_num % 4
        return relevant_sub_device_manager, relevant_entity

class sub_device_manager():
    def __init__(self): 
        self.entity_list = [0] * 4

    def set_entity_to_value(self, entity_num, required_value):
        self.entity_list[entity_num] = required_value
        print("I set the entity to : {}".format(required_value))

# This is used for auto completion purposes (Using pep convention)
class auto_complete_class(system_manager, device_manager, sub_device_manager):
    pass


system_instance = system_manager() # type: auto_complete_class
system_instance.set_entity_to_value(0, 3)

这个解决方案还有一个小问题,自动完成将无法工作,因为最高级别 class 几乎没有静态实现的功能。 为了解决这个问题,我做了一些欺骗,我创建了一个空的 class,它继承自所有层,并使用 pep 约定向 IDE 声明它是正在创建的实例的类型(#type: auto_complete_class).

我的看法是,如果你想动态配置系统的不同部分,你需要某种寻址方式,所以如果你输入一个带有某些参数的 ID 或地址,系统就会知道你在哪个子系统上的地址关于然后使用参数配置该系统。

OOP 对此非常好,然后您可以通过 bitwise operators.

轻松操作此类数据

所以基本寻址是通过二进制系统完成的,所以要在 python 中做到这一点,您首先需要为您的 class 实现地址静态属性,如果系统增长,可能还有一些基本的进一步细节。

地址系统的基本实现如下:

bin(71)
1010 1011
and if we divide it into nibbles 
1010 - device manager 10
1011 - sub device manager 11

所以在这个例子中我们有 15 个设备管理器和 15 个子设备管理器的系统,每个设备和子设备管理器都有它的整数 address.So 假设你想用子设备管理器访问设备管理器 10 11号。你需要他们的地址,它是二进制 71,你会选择:

system.config(address, parameter )

其中 system.config 函数如下所示:

def config(self,address, parameter):  
    device_manager = (address&0xF0)>>4 #10  
    sub_device_manager = address&0xf  # 11  
    if device_manager not in range(self.devices): raise LookupError("device manager not found")
    if sub_device_manager not in range(self.devices[device_manager].device): raise LookupError("sub device manager not found")

    self.devices[device_manager].device[sub_device_manager].implement(parameter)  

在外行人中,您会告诉系统设备 10 的 sub_device 11 需要使用此 参数 进行配置。

那么这个设置在 python 继承 class 的某些基础 class 系统中看起来如何 composited/inherited 到不同的 classes :

class systems(object):

    parent = None #global parent element, defaults to None well for simplicity

    def __init__(self):
        self.addrMASK = 0xf # address mask for that nibble
        self.addr = 0x1 # default address of that element
        self.devices = [] # list of instances of device 
        self.data = {  #some arbitrary data
            "param1":"param_val",
            "param2":"param_val",
            "param3":"param_val",
        }


    def addSubSystem(self,sub_system): # connects elements to eachother

        # checks for valiability
        if not isinstance(sub_system,systems):
            raise TypeError("defined input is not a system type") # to prevent passing an integer or something

        # appends a device to system data 
        self.devices.append(sub_system)

        # search parent variables from sub device manager to system
        obj = self
        while 1:
            if obj.parent is not None:
                obj.parent.addrMASK<<=4 #bitshifts 4 bits
                obj.parent.addr <<=4 #bitshifts 4 bits
                obj = obj.parent
            else:break

        #self management , i am lazy guy so i added this part so i wouldn't have to reset addresses manualy
        self.addrMASK <<=4 #bitshifts 4 bits
        self.addr <<=4 #bitshifts 4 bits

        # this element is added so the obj address is coresponding to place in list, this could be done more eloquently but i didn't know what are your limitations
        if not self.devices:
            self.devices[ len(self.devices)-1 ].addr +=1        
        self.devices[ len(self.devices)-1 ].parent = self

    # helpful for checking data ... gives the address of system
    def __repr__(self):

        return "system at {0:X}, {1:0X}".format(self.addr,self.addrMASK)

    # extra helpful lists data as well
    def __str__(self):
        data = [ '{} : {}\n'.format(k,v) for k,v in self.data.items() ]
        return " ".join([ repr(self),'\n',*data ])

    #checking for data, skips looping over sub systems
    def __contains__(self,system_index):

        return system_index-1 in range(len(self.data))

    # applying parameter change  -- just an example
    def apply(self,par_dict):
        if not isinstance(par_dict,dict): 
            raise TypeError("parameter must be a dict type")
        if any( key in self.data.keys() for key in par_dict.keys() ):

            for k,v in par_dict.items():
                if k in self.data.keys():
                    self.data[k]=v
                else:pass

        else:pass

    # implementing parameters trough addresses
    def implement(self,address,parameter_dictionary):

        if address&self.addrMASK==self.addr:

            if address-self.addr!=0:
                item = (address-self.addr)>>4
                self.devices[item-1].implement( address-self.addr,parameter_dictionary )
            else:
                self.apply(parameter_dictionary)






a = systems()
b = systems()
a.addSubSystem(b)
c = systems()
b.addSubSystem(c)

print('a')
print(a)
print('')

print('b')
print(b)
print('')

print('c')
print(c)
print('')

a.implement(0x100,{"param1":"a"})
a.implement(0x110,{"param1":"b"})
a.implement(0x111,{"param1":"c"})

print('a')
print(a)
print('')

print('b')
print(b)
print('')

print('c')
print(c)
print('')