使用接口或抽象类分离定义和实现的更好方法是什么?
实际上我不喜欢将引用计数对象与其他对象混合.我想在保持大项目时这可能成为一场噩梦.
但有时我需要从2个或更多类/接口派生一个类.
你有什么经历?
假设我定义了一个这样的抽象基类:
from abc import abstractmethod, ABCMeta
class Quacker(object):
__metaclass__ = ABCMeta
@abstractmethod
def quack(self):
return "Quack!"
Run Code Online (Sandbox Code Playgroud)
这确保了派生的任何类Quacker必须实现该quack方法.但是,如果我定义以下内容:
class PoliteDuck(Quacker):
def quack(self, name):
return "Quack quack %s!" % name
d = PoliteDuck() # no error
Run Code Online (Sandbox Code Playgroud)
我被允许实例化该类,因为我提供了quack方法,但函数签名不匹配.我可以看到这在某些情况下可能有用,但我有兴趣确保我可以肯定地调用抽象方法.如果功能签名不同,这可能会失败!
那么:我如何强制执行匹配的函数签名?如果签名不匹配,我会期望在创建对象时出错,就像我根本没有定义它一样.
我知道这不是惯用的,如果我想要这些类型的保证,那么Python是错误的语言,但这是不可能的 - 这是可能的吗?
mypy v0.910 拒绝 Python 3.9 中的抽象数据类。这是最小的可重复示例:
from abc import ABC, abstractmethod
from dataclasses import dataclass
@dataclass
class Liquid(ABC):
@abstractmethod
def drip(self) -> None:
pass
Run Code Online (Sandbox Code Playgroud)
这是错误消息:
$ mypy --python-version 3.9 so.py
so.py:4: error: Only concrete class can be given where "Type[Liquid]" is expected
Found 1 error in 1 file (checked 1 source file)
Run Code Online (Sandbox Code Playgroud)
如何让此代码通过mypy?
笔记
我从mypy 问题 #5374中了解到,这是mypy中的一个错误,于 2018 年首次注意到,但仍未得到纠正。不过,我认为人们必须将mypy与抽象数据类一起使用,因此必须有一种解决方法或正确的方法来定义或注释该类。有什么推荐的?
错误消息的基础似乎是mypy假设任何类型的对象都Type可以实例化,但抽象类不能实例化。这似乎是错误,因为Type被定义为表示类对象,而不一定是具体的类对象(即可以实例化的类对象)。
添加# type: ignore到包含的行 …
python abstract-base-class mypy python-dataclasses python-typing
在阅读了最优秀的"Head First Design Patterns"一书之后,我开始向同事们传播模式和设计原则的好处.在颂扬我最喜欢的模式的优点 - 策略模式 - 我被问到一个让我停顿的问题.当然,策略使用继承和组合,当我的同事问"为什么使用抽象基类而不是具体的类?"时,我在其中一个关于"程序到接口(或超类型)而不是实现"的长篇大论. .
我只能提出"你强迫你的子类实现抽象方法并阻止它们实例化ABC".但说实话,这个问题让我想起了gaurd.
我正在创建一个具有多种不同节点类型的树:二进制节点,一元节点和终端节点.我有一个所有节点继承的ABC.我正在尝试为树编写递归复制构造函数,如下所示:
class gpnode
{
public:
gpnode() {};
virtual ~gpnode() {};
gpnode(const gpnode& src) {};
gpnode* parent;
}
class bnode:gpnode
{
public:
bnode() {//stuff};
~bnode() {//recursive delete};
bnode(const bnode& src)
{
lnode = gpnode(src.lnode);
rnode = gpnode(src.rnode);
lnode->parent = this;
rnode->parent = this;
}
gpnode* lnode;
gpnode* rnode;
}
class unode:gpnode
{
public:
unode() {//stuff};
~unode() {//recursive delete};
unode(const unode& src)
{
node = gpnode(src.node);
node->parent = this;
}
gpnode* node;
}
Run Code Online (Sandbox Code Playgroud)
我的问题是我做不到
node = gpnode(src.node);
Run Code Online (Sandbox Code Playgroud)
因为gpnode是一个虚拟类.我可以
node = unode(src.node);
Run Code Online (Sandbox Code Playgroud)
但是当unode的子节点是bnode时,这不起作用.如何让它智能地调用我需要它的复制构造函数?
假设我有一个带有抽象基类的异常类,如下所示:
class MyExceptions(BaseExeption, metaclass=abc.ABCMeta):
pass
class ProperSubclass(MyExceptions):
pass
MyExceptions.register(ValueError)
Run Code Online (Sandbox Code Playgroud)
看来,我能赶上ProperSubclass的MyExceptions,但不是ValueError:
try:
raise ProperSubclass()
except MyExceptions:
print('As expected, control comes here...')
except:
print('...and not here.')
try:
raise ValueError()
except MyExceptions:
print('Control does not come here...')
except ValueError:
print('...but unexpectedly comes here.')
Run Code Online (Sandbox Code Playgroud)
所以我的问题是,我是否应该能够通过抽象基类捕获内置异常?如果是这样,怎么样?如果没有,那么规则是什么?
我想提出这个的另一种方式是:为匹配做,除非条款正确使用isinstance()/ issubclass(),如果没有(如出现这种情况)什么做他们使用?也许在C实现中有一些阴暗的快捷方式.
我有一个抽象的基类(Base),它有一些为它定义的堆栈特征(StackingTrait).
trait Base {
def foo
}
trait StackingTrait extends Base {
abstract override def foo { super.foo }
}
Run Code Online (Sandbox Code Playgroud)
使用以下语法实现子类非常方便,但是这不起作用,因为编译器说foo需要声明,override然后abstract override重新编译,这是一个无效的因为Impl是一个类.
class Impl extends Base with StackingTrait {
def foo {}
}
Run Code Online (Sandbox Code Playgroud)
我想不出为什么不允许这样的语法的一个很好的理由; foo在逻辑上被定义为Impl使得在概念上发生堆叠的顺序保持不变.
注意:我发现这种解决方法可以有效地完成我想要的相同操作,但是帮助类的必要性使我想要一个更好的解决方案.
class ImplHelper extends Base {
def foo {}
}
class Impl extends ImplHelper with StackingTrait
Run Code Online (Sandbox Code Playgroud)
为什么没有编译所需的语法,是否有一个优雅的解决方案?
我试图了解使用抽象基类的好处.考虑这两段代码:
抽象基类:
from abc import ABCMeta, abstractmethod, abstractproperty
class CanFly:
__metaclass__ = ABCMeta
@abstractmethod
def fly(self):
pass
@abstractproperty
def speed(self):
pass
class Bird(CanFly):
def __init__(self):
self.name = 'flappy'
@property
def speed(self):
return 1
def fly(self):
print('fly')
b = Bird()
print(isinstance(b, CanFly)) # True
print(issubclass(Bird, CanFly)) # True
Run Code Online (Sandbox Code Playgroud)
简单继承:
class CanFly(object):
def fly(self):
raise NotImplementedError
@property
def speed(self):
raise NotImplementedError()
class Bird(CanFly):
@property
def speed(self):
return 1
def fly(self):
print('fly')
b = Bird()
print(isinstance(b, CanFly)) # True
print(issubclass(Bird, CanFly)) …Run Code Online (Sandbox Code Playgroud) 我有这个简单的代码,我得到一个奇怪的错误:
from abc import ABCMeta, abstractmethod
class CVIterator(ABCMeta):
def __init__(self):
self.n = None # the value of n is obtained in the fit method
return
class KFold_new_version(CVIterator): # new version of KFold
def __init__(self, k):
assert k > 0, ValueError('cannot have k below 1')
self.k = k
return
cv = KFold_new_version(10)
In [4]: ---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-4-ec56652b1fdc> in <module>()
----> 1 __pyfile = open('''/tmp/py13196IBS''');exec(compile(__pyfile.read(), '''/home/donbeo/Desktop/prova.py''', 'exec'));__pyfile.close()
/home/donbeo/Desktop/prova.py in <module>()
19
20
---> 21 cv = KFold_new_version(10) …Run Code Online (Sandbox Code Playgroud) 我正在尝试定义一个类,该类将另一个类作为属性_model并将实例化该类的对象。
from abc import ABC
from typing import Generic, TypeVar, Any, ClassVar, Type
Item = TypeVar("Item", bound=Any)
class SomeClass(Generic[Item], ABC):
_model: ClassVar[Type[Item]]
def _compose_item(self, **attrs: Any) -> Item:
return self._model(**attrs)
Run Code Online (Sandbox Code Playgroud)
self._model(**attrs)我认为返回 , 的实例应该是显而易见的Item,因为_model被显式声明为Type[Item]并被attrs声明为Dict[str, Any]。
但我从中得到的mypy 0.910是:
test.py: note: In member "_compose_item" of class "SomeClass":
test.py:11: error: Returning Any from function declared to return "Item"
return self._model(**attrs)
^
Run Code Online (Sandbox Code Playgroud)
我究竟做错了什么?
python ×6
inheritance ×2
mypy ×2
architecture ×1
c# ×1
c++ ×1
delphi ×1
exception ×1
interface ×1
java ×1
mixins ×1
oop ×1
scala ×1
traits ×1
type-hinting ×1