有没有一些类似的替代方法来同时使用classmethod和property装饰器?

huangapple go评论79阅读模式
英文:

What are some similar alternatives to using classmethod and property decorators together?

问题

有哪些类似的替代方法可以替代同时使用classmethod和property装饰器?

在Python 3.11及更高版本中,不再支持将它们结合使用,详情请参考:https://docs.python.org/3.11/library/functions.html#classmethod

我有如下的代码:

class Bike:
  @classmethod
  @property
  def tire_type(cls) -> tire_type.Road:
    return tire_type.Road

from . import tire_type

tire_type导入必须放在最后,因为它与当前模块存在循环依赖关系。

有哪些选项可以为Bike类提供tire属性,而不将这两个装饰器组合在一起?

我还希望在VSCode中正确显示tire_type的类型提示。

英文:

What are some similar alternatives to using classmethod and property decorators together?

In python 3.11 and later, combining them is no longer supported per: https://docs.python.org/3.11/library/functions.html#classmethod

I have code like so:

class Bike:
  @classmethod
  @property
  def tire_type(cls) -> tire_type.Road:
    return tire_type.Road

from . import tire_type

tire_type import must be last because it has a cyclic dependency on the current module.
What are some options for providing the tire property in the Bike class that does not combine the two decorators?

I also want the tire_type type hint to show up correctly in vscode.

答案1

得分: 2

你可以随时实现自己的描述符。这是一个简单的描述符,假设你要装饰的方法是一个“getter”:

class classproperty:
    def __init__(self, method):
        self.method = method
    def __get__(self, obj, cls=None):
        if cls is None:
            cls = type(obj)
        return self.method(cls)

class Foo:
    bar = 42
    @classproperty
    def baz(cls):
        return cls.bar * 2

class FooSpecial(Foo):
    bar = 11

Foo.baz
84
FooSpecial.baz
22

classmethodproperty都是作为描述符实现的。你可以阅读有关描述符协议如何工作的更多信息在描述符指南中。该指南实际上展示了propertyclassmethod的纯Python实现,如果需要进一步了解,可以参考这些内容。

英文:

You can always implement your own descriptor. Here is a bare-bones one that assumes the method you are decorating is a "getter":

>>> class classproperty:
...     def __init__(self, method):
...         self.method = method
...     def __get__(self, obj, cls=None):
...         if cls is None:
...             cls = type(obj)
...         return self.method(cls)
...
>>> class Foo:
...     bar = 42
...     @classproperty
...     def baz(cls):
...         return cls.bar * 2
...
>>> class FooSpecial(Foo):
...     bar = 11
...
>>> Foo.baz
84
>>> FooSpecial.baz
22

Both classmethod and property are implemented as descriptors. You can read more about how the descriptor protocol works in the Descriptor HowTo which actually shows pure python implementations of property and classmethod which you can consult if you need to elaborate on the above.

答案2

得分: 0

以下是翻译好的部分:

  1. 编写一个自定义描述符,使用泛型来返回包装的类型
  2. 使方法成为类方法,这保持了类访问,但需要使用 ()
  3. 将其设置为静态方法,但需要使用 ()
  4. 将类设置为数据类,并使用 default_factory 返回所需的类

首选选项是1或4,因为它们不需要 () 并且允许对属性进行类型描述以及在访问 Bike 或实例化它时延迟评估属性。

英文:

Some options are

  1. Write a custom descriptor that uses a generic to return the wrapped type
T = typing.TypeVar('T')

class classprop(typing.Generic[T]):
    def __init__(self, method: typing.Callable[..., T]):
        self.method = method
        functools.update_wrapper(self, method) # type: ignore

    def __get__(self, obj, cls=None) -> T:
        if cls is None:
            cls = type(obj)
        return self.method(cls)


class Bike:
    @classprop
    def tire_type(cls) -> typing.Type[tire_type.Road]:
        return tire_type.Road

print(Bike.tire_type)
  1. Make the method classmethod only, this keeps class access but () are required
  2. Make it staticmethod but () are required
  3. Make the class a dataclass and use default_factory to return the desired class
@dataclasses.dataclass
class Bike
    tire_type: typing.Type[tire_type.Road] = dataclasses.field(
        default_factory=lambda: tire_type.Road
    )


print(Bike().tire_type)

Option 1 or 4 is preferred because it does not need () and it allows a type discriptions of the property and delayed evaluation of the property later when accessing Bike or instantiating it

huangapple
  • 本文由 发表于 2023年6月1日 10:49:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/76378373.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定