我应该使用元素,班级装饰器或覆盖__new__方法吗?

问题描述 投票:0回答:4

这里是我的问题。  我希望以下课程具有一堆属性属性。  我可以像
foo
bar
一样将它们全部写出来,也可以基于我见过的其他示例,看起来我可以使用类装饰器,元口,或覆盖
__new__

方法来自动设置属性。 我只是不确定“正确”的方法是什么。
class Test(object):
    def calculate_attr(self, attr):
        # do calculaty stuff
        return attr

    @property
    def foo(self):
        return self.calculate_attr('foo')

    @property
    def bar(self):
        return self.calculate_attr('bar')
python inheritance metaclass
4个回答
5
投票

魔术很糟糕。它使您的代码更难理解和维护。您几乎不需要元素或

__new__

看起来您的用例可以用非常简单的代码实现(只有一小段魔术):

class Test(object):
    def calculate_attr(self, attr):
        return something

    def __getattr__(self, name):
        return self.calculate_attr(name)

3
投票

元元素的

__new__
不会成为您所做的班级的
__new__
,它是用来使课程本身的。 Metaclass返回了实际的类对象。一类新实例由__new__
返回。

考虑以下(疯狂)代码:

def MyMetaClass(name, bases, dict): print "name", name print "bases", bases print "dict", dict return 7 class C('hello', 'world'): __metaclass__ = MyMetaClass foo = "bar" def baz(self, qux): pass print "C", C

(我使用一个函数而不是类作为元类。任何可呼叫都可以用作元素,但是许多人选择将其纠正为class,将其作为从
type

继承的类,而新的被压倒了。 IT输出

name C
bases ('hello', 'world')
dict {'baz': <function baz at 0x4034c844>, '__module__': '__main__', 'foo': 'bar', '__metaclass__': <function MyMetaClass at 0x40345c34>}
C 7

可以帮助您更好地理解什么元素

您很少需要定义自己的元素。

当创建新类 - 非实例 - 时,使用

麦克拉类。这样,您可以例如注册类(Django做到这一点,并使用它例如在数据库中创建表)。由于class是一种指示,您可以将其视为课堂的装饰师。

您的问题明确排除了另一种可能性:_

Init_subclass

1
投票
_

是由python中的

Pep-487
介绍的3.6.6.

this解决了一个循环类型的问题,该问题不受您提到的任何其他方法涵盖的问题,因为在包括基类的名称空间之前,必须定义装饰器和元组。

0
投票

from __future__ import annotations from typing import ( ClassVar, List, get_type_hints, get_origin, Annotated, ) class ModelMetaclass(type): def __new__(metaclass, name, bases, attrs): new_cls = type.__new__(metaclass, name, bases, attrs) hints = get_type_hints(new_cls, include_extras=True) for ( parameter_name, parameter_annotation, ) in hints.items(): if get_origin(parameter_annotation) is Annotated: new_cls._parser_cfg.append(parameter_name) return new_cls class BaseClass(metaclass=ModelMetaclass): elements: List[BaseClass] _parser_cfg: ClassVar[List[str]] _parser: ClassVar[BaseClass] class DerivedClassA(BaseClass): pass a = DerivedClassA() 尽管从键入描述的角度来看,此代码是正确的,但它在运行时失败: Traceback (most recent call last): .... eval(self.__forward_code__, globalns, localns), ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "<string>", line 1, in <module> NameError: name 'BaseClass' is not defined 这是由get_type_hints之间的循环依赖性引起的,该依赖性试图解析

BaseClass.elements

的声明中使用的基本类型。

可以通过使用

__init_subclass__

可以很好地解决该问题。 等效的代码是:
from __future__ import annotations
from typing import (
    ClassVar,
    List,
    get_type_hints,
    get_origin,
    Annotated,
)


class ModelMetaclass(type):
    def __new__(metaclass, name, bases, attrs):
        new_cls = type.__new__(metaclass, name, bases, attrs)
        hints = get_type_hints(new_cls, include_extras=True)
        for (
            parameter_name,
            parameter_annotation,
        ) in hints.items():
            if get_origin(parameter_annotation) is Annotated:
                new_cls._parser_cfg.append(parameter_name)
        return new_cls


class BaseClass(metaclass=ModelMetaclass):
    elements: List[BaseClass]
    _parser_cfg: ClassVar[List[str]]
    _parser: ClassVar[BaseClass]

class DerivedClassA(BaseClass):
    pass

a = DerivedClassA()

在运行时,没有更多的错误,输出为:
{'_parser': typing.ClassVar[__main__.BaseClass],
 '_parser_cfg': typing.ClassVar[typing.List[str]],
 'elements': typing.List[__main__.BaseClass]}

	

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.