【问题标题】:Is there a way to reference an object's base __class__ from class scope?有没有办法从类范围引用对象的基类 __class__ ?
【发布时间】:2021-03-13 19:49:53
【问题描述】:

我有一系列的类,继承自几个可能的父类中的一个,它们都共享一个基类。每个类都有一个类范围 Dict[str, object] parameters,它基于基类 parameters

from copy import deepcopy


class BaseClass:
    parameters = {
        'example_param1': ParamObject(name='example_param1', editable=True),
    }


class MiddleClass(Baseclass):
    parameters = {
        **deepcopy(Baseclass.parameters),
        'example_param2': ParamObject(name='example_param2', editable=False),
    }


class ChildClass(MiddleClass):
    parameters = {
        **deepcopy(MiddleClass.parameters),
        'example_param3': ParamObject(name='example_param3', editable=True),
    }

这个实现可以完成这项工作,但我发现**deepcopy(Baseclass.parameters), 行不令人满意。随着时间的推移,这些子类将由仅对编码有基本了解的人进行编辑,因此我希望使代码尽可能简单且可剪切和粘贴。

我可以在课堂范围内调用什么来获得super().__class__ 的等价物吗?我希望用户能够将基类从 MiddleClass 更改为 MiddleClass2,而无需记住在多个位置更改基类。

【问题讨论】:

  • 请注意,super().__class__ 也不会做你想做的事,即使在实例方法中 - 它只会提供 super
  • 这可能是__init_subclass__ 的情况,尽管我不清楚如何最好地构建它。
  • 参数是否必须在字典中?如果它们是类的直接属性,您将免费获得所需的继承行为。如果它们必须与其他类属性区分开来,那么命名约定(例如您的示例中的*_param*)可能就足够了。
  • 我会为此使用元类。您可以走 MRO 并结合所有这些参数。
  • @alkasm __init_subclass__ 的存在是为了避免必须为此类用例使用元类。

标签: python python-3.x oop superclass


【解决方案1】:

您可以使用元类,例如建议的其他答案,但我不确定对于新程序员来说理解起来有多容易。

您可以通过在类上调用__bases__ 来访问类继承自的类。可悲的是,在 python 上,你不能引用你在类主体上创建的类,这意味着你不能这样做:

class A:
   bases = A.__bases__  # Invalid code, A is not defined yet

如果你想这样做,在保持参数静态的同时,你可以在类定义之后这样做,它会有你正在寻找的确切行为,虽然有点难看:

class BaseClass:
    parameters = {
        'example_param1': ParamObject(name='example_param1', editable=True),
    }


class MiddleClass(Baseclass):
    pass

MidleClass.parameters = {
    **deepcopy(MidleClass.__bases__[0].parameters),
    'example_param2': ParamObject(name='example_param2', editable=False),
}

现在,稍微改进一下,如果你的 parameters 真的总是一个 'Dict[str, object]',你不需要做一个 deepcopy,因为使用 ** 是一种功能性的方式.您还可以使用__bases__ 值处理多个父类,以确保捕获每个值。所以,如果我要这样做,我会这样做:

def base_parameters(cls):
    parameters = {}
    for base in cls.__bases__:
        parameters.update(base.parameters)

    return parameters


class BaseClass:
    parameters = {
        'example_param1': ParamObject(name='example_param1', editable=True),
    }


class MiddleClass(BaseClass):
    pass

MiddleClass.parameters = {
    **base_parameters(MiddleClass),
    'example_param2': ParamObject(name='example_param2', editable=False),
}


class ChildClass(MiddleClass):
    pass

ChildClass.parameters = {
    **base_parameters(ChildClass),
    'example_param3': ParamObject(name='example_param3', editable=True),
}

【讨论】:

    【解决方案2】:

    您可以使用__init_subclass__ 将父类中的参数直接添加到显式定义的parameters 属性中,代价是插入顺序被颠倒(如果有的话)。

    class ParameterBase:
        def __init_subclass__(cls, **kwargs):
            super().__init_subclass__(**kwargs)
            for pc in cls.__bases__:
                d = getattr(pc, 'parameters', {})
                cls.parameters.update(d)
    
    class BaseClass(ParameterBase):
        parameters = {
            'example_param1': ParamObject(name='example_param1', editable=True),
        }
    
    
    class MiddleClass(BaseClass):
        parameters = {
            'example_param2': ParamObject(name='example_param2', editable=False),
        }
    
    
    class ChildClass(MiddleClass):
        parameters = {
            'example_param3': ParamObject(name='example_param3', editable=True),
        }
    

    【讨论】:

    • 在这种情况下,订单虽然不是什么大问题,但会在其他地方引起一些不直观的副作用。我会提到虽然我不知道 init_subclass 存在并且我已经在考虑其他可以帮助我的地方,所以感谢您的回答!
    【解决方案3】:

    您可以使用元类来做到这一点。检查继承树中的所有类以查看它们是否具有parameters 字段。如果是,则将它们合并在一起并在类实例上设置属性。

    class ParamMeta(type):
        def __init__(cls, name, bases, dct):
            class_types = [cls] + list(bases)
            parameters = {}
            for class_type in class_types:
                if hasattr(class_type, "parameters"):
                    parameters.update(class_type.parameters)
            cls.parameters = parameters
            super().__init__(name, bases, dct)
    

    例子:

    class Foo(metaclass=ParamMeta):
        parameters = {"a": "b"}
    
    
    class Bar(Foo):
        pass
    
    
    class Fizz(Bar):
        parameters = {"c": "d"}
    
    
    print(Foo.parameters)
    print(Bar.parameters)
    print(Fizz.parameters)
    

    输出:

    {'a': 'b'}
    {'a': 'b'}
    {'c': 'd', 'a': 'b'}
    

    【讨论】:

      【解决方案4】:

      如果你的类只继承一个类,你可以使用元类:

      class Meta(type):
          def __new__(cls, name, parents, namespace):
              namespace['parameters'] = {
                  **deepcopy(parents[0].parameters),
                  **namespace['parameters']
              }
      
              return super().__new__(cls, name, parents, namespace)
      
      class MiddleClass(Baseclass, metaclass=Meta):
          parameters = {
              'example_param2': ParamObject(name='example_param2', editable=False),
          }
      

      这当然是一个简化版本,例如不处理多个父类。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-10-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-05-21
        相关资源
        最近更新 更多