Python 3.11+ 中的类属性

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

在 Python 3.9 中,我们获得了链接

@classmethod
@property
来明智地创建类属性的能力。

class Foo:
  @property
  def instance_property(self):
    return "A regular property"

  @classmethod
  @property
  def class_property(cls):
    return "A class property"

这是通过给予

@classmethod
与描述符协议适当的交互来实现的,这意味着一个人的前景不仅限于
@property
而是阳光下的任何描述符。一切都很好,直到发现该实现导致了“许多下游问题”,并在 Python 3.11 中弃用。

我已经阅读了一些关于弃用的GitHub 讨论,并且不会在这里抱怨我所谓的仓促撤回仓促设计。事实上,类属性是人们想要的合理的东西,并且可以在 Python 3.9/3.10 中使用,但现在不能。发行说明建议如下:

要“传递”类方法,请考虑使用 Python 3.10 中添加的 __wrapped__ 属性。

称这样的句子本身极其无益,并不会引起争议。描述符协议不是普通用户需要或想要遇到的东西,因此通过自定义实现将

@classmethod
与他们链接肯定是那些了解情况的人可以并且会花时间弄清楚如何正确执行的事情3.11+.

但是对于那些不知道

@property
除了让他们去掉括号的东西之外还有什么的人来说,如何在 Python 3.11+ 中定义类属性,特别是如何做到

python python-3.x properties python-decorators python-descriptors
2个回答
5
投票

尽管如此,就像我在 3.9 之前所做的那样:自定义“属性”重写。

问题是,“属性”可以做很多事情,如果需要其中的所有内容,那就需要很多代码。

我想可以将

property
本身进行子类化,这样我们就可以获得额外的
.class_getter
装饰器。

显然,类设置器将涉及自定义元类或

__setattr__
的特化。

让我们看看我是否可以带一个相当短的

classproperty

[稍作修改后]

因此,事实证明,简单地继承属性并为“类 getter”添加装饰器并不容易实现——“属性”在编写时并没有考虑到子类化和扩展其功能。

因此,“简单”的事情和子集是编写一个自定义描述符装饰器,它只会将单个方法转换为类获取器 - 根本不支持 set、del 或继承。

另一方面,代码又短又简单:

class classproperty:
    def __init__(self, func):
        self.fget = func
    def __get__(self, instance, owner):
        return self.fget(owner)

这就像预期的那样工作:


In [19]: class A:
    ...:     @classproperty
    ...:     def test(cls):
    ...:         return f"property of {cls.__name__}"
    ...: 

In [20]: A.test
Out[20]: 'property of A'

另一种方式,如果想要一直拥有一个类属性设置器,只需在自定义元类上编写一个普通属性(该元类可以仅用于保存所需的属性)。然而,这种方法将使属性在实例上不可见 - 它们仅适用于类本身:


In [22]: class MetaA(type):
    ...:     @property
    ...:     def test(cls):
    ...:         return cls._test
    ...:     @test.setter
    ...:     def test(cls, value):
    ...:         cls._test = value.upper()
    ...: 

In [23]: class A(metaclass=MetaA):
    ...:     pass
    ...: 

In [24]: A.test = "hello world!"

In [25]: A.test
Out[25]: 'HELLO WORLD!'

In [26]: A().test
--------------------------------------------------------------
...
AttributeError: 'A' object has no attribute

0
投票

我认为这是一个非常好的问题,我希望有更好的答案。我能找到的我最喜欢的方法是使用

__init_subclass__
来自这个答案

© www.soinside.com 2019 - 2024. All rights reserved.