这里是我的问题。 我希望以下课程具有一堆属性属性。 我可以像
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')
魔术很糟糕。它使您的代码更难理解和维护。您几乎不需要元素或
__new__
。
看起来您的用例可以用非常简单的代码实现(只有一小段魔术):
class Test(object):
def calculate_attr(self, attr):
return something
def __getattr__(self, name):
return self.calculate_attr(name)
元元素的
__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是由python中的
Pep-487介绍的3.6.6. this解决了一个循环类型的问题,该问题不受您提到的任何其他方法涵盖的问题,因为在包括基类的名称空间之前,必须定义装饰器和元组。
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]}