python - 能够在抽象类的子类中设置属性
问题描述
我已经为此奋斗了半个小时,所以我已经通过了自己尝试半个小时的规则,并寻求您的帮助。我试图让 Child 使用抽象类的 setter 抽象方法,但它不起作用......
#!/usr/bin/env python3
from abc import ABC, abstractmethod
from typing import List
class Indicator(ABC):
def __init__(self, **kwargs):
super().__init__()
pass
@abstractmethod
def calculate(self):
"""
kwargs in children will most likely be date_from, date_to, index
"""
raise NotImplementedError("The calculate method is not implemented!")
@property
@abstractmethod
def db_ids(self):
return self._db_ids
@db_ids.setter
@abstractmethod
def db_ids(self, ids: List[int]):
assert isinstance(ids, list)
assert all(isinstance(id_, int) for id_ in ids)
self._db_ids = ids
@property
@abstractmethod
def name(self):
return self._name
@name.setter
@abstractmethod
def name(self, set_name: str):
assert isinstance(set_name, str)
self._name = set_name
# …………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………………
class ValueHistorical(Indicator):
def __init__(self, **kwargs):
if kwargs:
self.kwargs = kwargs
super(ValueHistorical, self).__init__(**kwargs)
self.db_ids = [119, 120, 121, 122]
self.name = 'Value Based on Historical'
@property
def db_ids(self):
return self._db_ids
@property
def name(self):
return self._name
def calculate(self):
pass
ValueHistorical(**{'date_from': '2010-01-01', 'date_to': '2012-01-01'})
这里的论点无关紧要。我得到的错误是AttributeError: can't set the attribute'
.
我想要实现的是在 ValueHistorical 构造函数内部,它转到它的父抽象类的 db_ids 设置器和分配它们时的名称。
解决方案
这实际上与 ABC 无关,但与您在子类中反弹属性但没有设置器的事实有关。这:
class ValueHistorical(Indicator):
@property
def db_ids(self):
return self._db_ids
@property
def name(self):
return self._name
只需将父属性替换为新属性,但将这些属性定义为只读,因为您没有提供设置器。
请记住,装饰器语法只是语法糖,所以:
@property
def getter(...): pass
只是一种更花哨的写作方式
def getter(...): pass
getter = property(getter)
由于 getter 和 setter 是property
实例的属性,所以在子类中重新定义属性时,不能只重新定义 getter,还必须重新定义 setter。
这里的一个常见模式是让 getter 和 setter(如果有的话)委托给另一种方法,因此您不必重新实现整个事情,即:
class Base(object):
@property
def foo(self):
return self._get_foo()
@foo.setter
def foo(self, value):
self._set_foo(value)
def _get_foo(self):
# ...
def _set_foo(self, value):
# ...
因此子类可以覆盖_get_foo
和/或_set_foo
不必重新定义属性。
此外,同时应用property
和abstractmethod
到一个函数是完全没用的。这:
@property
@abstractmethod
def db_ids(self):
return self._db_ids
相当于
def db_ids(self):
return self._db_ids
db_ids = property(abstractmethod(db_ids))
所以 ABC 将在这里看到的是属性 - 它的 getter(和/或 setter)已被装饰的事实abstractmethod
被忽略,ABC 不会检查属性的 getter 和 setter。如果你把它们反过来,即
db_ids = abstractmethod(property(db_ids))
那么你根本就没有定义一个属性(实际上,它根本不起作用——你会从一开始就得到一个异常,“'property'对象没有属性' isabstractmethod '”)
FWIW,abstractmethod
装饰器仅用于未定义的方法(空体),因此子类必须实现它们。如果您有默认实现,请不要将其标记为抽象,否则为什么要提供默认实现?
编辑:
您在评论中提到(关于已删除的答案):
我基本上希望 ValueHistorical 在 ValueHistorical 构造函数中分配 db_ids 和 name 时转到 Abstract 类的 setter 方法
然后最简单的解决方案就是我上面解释的那个:为 getter 和/或 setter 定义实现方法(您可以根据需要将它们中的任何一个或两者都抽象化)并使用具体属性来调用这些实现方法。
哦,是的:assert
是一个开发工具,不要将它用于生产代码中的类型检查。如果你真的想做类型检查(这有时是有意义的,但通常不是完全浪费时间),使用isinstance
并提出一个TypeError
. 例如,您的db_ids
setter 应如下所示:
if not isinstance(ids, list):
raise TypeError("ids should be a list")
if not all(isinstance(id_, int) for id_ in ids)
raise TypeError("ids items should be ints")
甚至更好:
# you don't care if it really was a list actually,
# as long as you can build a list out of it, and
# you don't care if it really contains ints as long
# as you can build ints out of them.
#
# No need for typecheck here, if `ids` is not iterable
# or what it yields cannot be used to build an int,
# this will raise, with way enough informations to
# debug the caller.
ids = [int(id) for id in ids)]
推荐阅读
- arrays - 将json数组转换为字符列表esql
- django - 如何解决 facebook django url social-auth-app-django 问题?
- javascript - 在反应中不能同时使用按钮和链接
- vue.js - PrimeVue 数据表过滤器
- javascript - 无法解构“pin”的属性“urls”,因为它未定义
- azure - Azure API 管理提供 200 [未完整发送(请参阅异常遥测)]
- r - 如何向 ggplot2 中的各种散点图添加指数趋势线?
- html - 一种形式内另一种形式变得不可点击
- html - Angular:Form-Submit 应该转发到新组件
- angular - 如何从 Angular 的模块文件中调用在服务内部编写的函数?