考虑此抽象类和实现它的类:
from abc import ABC
class FooBase(ABC):
foo: str
bar: str
baz: int
def __init__(self):
self.bar = "bar"
self.baz = "baz"
class Foo(FooBase):
foo: str = "hello"
这里的想法是,将需要实现 foobase
的 foo
才能指定 foo 属性,但是其他属性( bar
和 baz
)不需要被覆盖,因为它们已经通过抽象类提供的方法处理。
从MyPy类型检查的角度来看,是否可以强制强制 foo
声明属性 foo
并引起类型检查错误否则?
编辑:
基本原理是 foobase
是库的一部分,应防止客户代码实现它,而无需指定 foo
的值。对于 bar
和 baz
,这些完全由库管理,客户不在乎它们。
Considering this abstract class and a class implementing it:
from abc import ABC
class FooBase(ABC):
foo: str
bar: str
baz: int
def __init__(self):
self.bar = "bar"
self.baz = "baz"
class Foo(FooBase):
foo: str = "hello"
The idea here is that a Foo
class that implements FooBase
would be required to specify the value of the foo
attribute, but the other attributes (bar
and baz
) would not need to be overwritten, as they're already handle by a method provided by the abstract class.
From a MyPy type-checking perspective, is it possible to force Foo
to declare the attribute foo
and raise a type-checking error otherwise?
EDIT:
The rationale is that FooBase
is part of a library, and the client code should be prevented from implementing it without specifying a value for foo
. For bar
and baz
however, these are entirely managed by the library and the client doesn't care about them.
发布评论
评论(3)
以前,此类问题的“解决方案”之一是堆叠
@property
,@classmethod
和@abstractmethod
一起产生“ 类属性。摘要 @classmethod 或
@staticmethod
@property
的表现真的很差,因此已经决定这样的链式装饰器是 reteprecated开始python 3.11 ,现在将使用mypy
的工具错误。如,特别是如果您需要属性来进行一些昂贵/延迟的访问。诀窍是使用
@abstractmethod
与子类typing.protocol
进行补充。请注意,尽管Linters可以捕获此类型的错误,但在运行时不会执行它,与创建
abc.abc
的子类不同,如果您尝试使用抽象属性实例化类,则会导致运行时错误。此外,上述方法不支持
foo = distibcriptor()
的使用,类似于用@property
而不是实现属性。要涵盖这两种情况,您需要使用以下内容:两个类通过类型检查,并且按预期的是在运行时实际工作,尽管在运行时又没有执行任何执行。
Previously, one the "solutions" to this type of problem was to stack
@property
,@classmethod
, and@abstractmethod
together to produce an "abstract class property`.According to CPython issue #89519, chaining descriptor decorators like
@classmethod
or@staticmethod
with@property
can behave really poorly, so it has been decided that chaining decorators like this is deprecated beginning Python 3.11, and will now error with tools likemypy
.There is an alternative solution if you really need something that behaves like an abstract class property, as explained in this comment, especially if you need a property for some expensive/delayed accessing. The trick is to supplement using
@abstractmethod
with subclassingtyping.Protocol
.Note that although linters can catch this type of error, it is not enforced at runtime, unlike creating a subclass of
abc.ABC
which causes a runtime error if you try to instantiate a class with an abstract property.Additionally, the above approach does not support the use of
foo = Descriptor()
, similar to implementing an attribute with a@property
instead. To cover both cases, you'll need to use the following:Both classes pass type checks and actually work at runtime as intended, although again nothing is enforced at runtime.
这是部分答案。您可以使用
它只是部分的,因为只有在没有初始化的
foo
的情况下尝试实例化foo
,您才会遇到mypy错误一个简单的
@abstractmethod
。只有在实例化时,它才会引起错误。这是可以预期的,因为foo
可能不打算作为具体类,并且本身可能会被分类。您可以通过说明它是带有typing.final.final
的具体类来减轻这种情况的。以下将在课程本身上引起错误。This is a partial answer. You can use
It's only partial because you'll only get a mypy error if you try to instantiate
Foo
without an initializedfoo
, likeThis is the same behaviour as when you have a simple
@abstractmethod
. Only when instantiating it is the error raised. This is expected becauseFoo
might not be intended as a concrete class, and may itself be subclassed. You can mitigate this somewhat by stating it is a concrete class withtyping.final
. The following will raise an error on the class itself.您可以使用 init_subclass __ ,而不是依靠用户在班级主体内部设置属性-0487/“ rel =” nofollow noreferrer“> pep 487 。此外,您应该将
typing.classvar
用于类变量,否则将与实例变量混合。此语法对于用户很干净,尤其是当您需要比设置类变量更复杂的东西时,
这些变量可以设置来创建
database.connection.connection
类变量。这种方法的一个缺点是,进一步的子类将需要继续提供类参数,通常可以通过实现自己的
__ init_subclass __
来修复,良好的linters应该能够识别这种类型的模式,生产产生的模式
class foo(foobase)上的错误:
而无需在class subdatabse上产生错误(数据库,reconnect = true):
。Rather than relying on the user to set an attribute inside of the class body, you can instead mandate a value using
__init_subclass__
as proposed by PEP 487. Additionally, you should usetyping.ClassVar
for class variables, otherwise it'll mix with instance variables.This syntax is clean for the user, especially when you need something more complex than setting a class variable like
which could get setup to create a
Database.connection
class variable.The one downside with this approach is that further subclasses will need to continue supplying the class parameters, which can usually be fixed by implementing your own
__init_subclass__
:Good linters should be able to recognize this type of pattern, producing errors on
class Foo(FooBase):
without producing errors onclass SubDatabse(Database, reconnect=True):
.