【问题标题】:Python create instance of derived classPython创建派生类的实例
【发布时间】:2020-12-27 16:47:24
【问题描述】:

我想要抽象类 Task 和一些派生类,如 TaskA、TaskB、... 我需要任务中的静态方法来获取所有任务并返回它们的列表。但问题是我必须以不同的方式获取每项任务。我希望 Task 是通用的,因此当我创建新类(例如 TaskC)时,它应该可以在不更改类 Task 的情况下工作。我应该使用哪种设计模式? 假设每个派生任务都有其唯一 id 的装饰器,我正在寻找可以通过 id 查找类并创建它的实例的函数。在python中怎么做?

【问题讨论】:

  • 静态方法需要在基类中吗?或者它可以是一个单独的东西?这听起来像是工厂模式。
  • 我希望它在基类中。我相信使用反射是可能的。我可以使用 __subclasses__ 获取所有派生类,但我不知道如何检查类是否具有具有特定 id 的装饰器。

标签: python reflection


【解决方案1】:

有几种方法可以实现这一目标。

第一个也是最简单的方法是使用__new__ 方法作为工厂来决定应该返回哪个子类。

class Base:
    UUID = "0"
    def __new__(cls, *args, **kwargs):
        if args == "some condition":
            return A(*args, **kwargs)
        elif args == "another condition":
            return B(*args, **kwargs)

class A(Base):
    UUID = "1"

class B(Base):
    UUID = "2"
    
instance = Base("some", "args", "for", "the", condition=True)

在这个例子中,如果你想确保类是由 uuid 选择的。您可以将 if 条件替换为类似

if a.UUID == "an argument you passed":
    return A

但这并不是真的有用。由于您了解特定的 UUID,因此您最好不要费心通过界面。

由于我不知道你想要装饰器做什么,所以我想不出一种方法来集成它。

编辑以解决注释:

你不需要每次都更新它,如果你能巧妙地表达你的表达。

假设定义因素来自一个配置文件,上面写着“使用 B 类”

for sub_classs in self.__subclasses__():
    if sub_class.UUID == config.uuid:
        return sub_class(*args, **kwargs) # make an instance and return it

问题在于 uuid 对我们人类没有用处。如果我们改为使用config.name 来替换示例中我们拥有 uuid 的每个地方,会更容易理解

【讨论】:

  • 问题在于你必须在基类中创建条件。例如,当您创建新的派生类时,您必须记住向 Base 添加新条件。
  • 已更新以回答更多问题
【解决方案2】:

我为此奋斗了很多时间,而这正是我想要的:

def class_id(id:int):
    def func(cls):
        cls.class_id = lambda : id
        return cls
    return func

def find_subclass_by_id(cls:type, id:int) -> type:
    for t in cls.__subclasses__():
        if getattr(t, "class_id")() == id:
            return t


def get_class_id(obj)->int:
    return getattr(type(obj), "class_id")()



class Task():
    def load(self, dict:Dict) -> None:
        pass

    @staticmethod
    def from_dict(dict:Dict) -> 'Task':
        task_type = int(dict['task_type'])
        t = find_subclass_by_id(Task, task_type)
        obj:Task = t()
        obj.load(dict)
        return obj


    @staticmethod
    def fetch(filter: Dict):
        return [Task.from_dict(doc) for doc in list_of_dicts]

@class_id(1)
class TaskA(Task):
     def load(self, dict:Dict) -> None:
        ...
     ...
 

【讨论】:

    猜你喜欢
    • 2013-11-16
    • 2014-09-29
    • 2023-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-05
    • 2023-03-19
    • 2021-02-28
    相关资源
    最近更新 更多