是否可以继承 wx.grid.Grid 并使用我自己的元类?
Is it possible to subclass wx.grid.Grid and also use my own metaclass?
我有一个显示各种网格的应用程序。网格具有不同种类的功能,所以我的设计是一个处理通用网格事物的基本网格 class,以及混合到具有基本网格的 class 中的各种功能混合:
class BaseGrid(wx.grid.Grid):
def foo():
return 0
class Grid_Mixin1():
def feature():
self.foo()
class Grid_Mixin2():
def feature():
self.foo()
class SpecificGrid(Mixin1, BaseGrid):
...
问题是我正在尝试使用类型提示,在 mixin 内部,类型检查器不知道 self.foo()
会存在,从而引发未知成员错误。
我决定使用协议让静态类型检查器知道 mixins 符合什么:
from typing import Protocol
class MyProtocol(Protocol):
def foo(self):
...
class Grid_Mixin1(MyProtocol):
def feature():
self.foo()
class Grid_Mixin2(MyProtocol):
def feature():
self.foo()
现在,当我尝试使用 SpecificGrid
时,我得到了臭名昭著的
TypeError: metaclass conflict: the metaclass of a derived class must
be a (non-strict) subclass of the metaclasses of all its bases
我只能假设 wx.grid.Grid 是从它自己的元 class 派生出来的?从文档中看,这对我来说并不明显,但这是我唯一的解释。我的评估正确吗?我该怎么做才能解决这个问题?
是的 - 它们有不同的元class,因此您必须创建一个组合的元class才能将两个class结合起来。
这比听起来容易 - 因为 wx 和 typing 都是维护良好的代码,并且在 class 布局中没有内在冲突,创建一个不冲突的 metaclass 只是一个问题结合 metaclasses.
然而,问题在于 typing.Protocol subclasses:这个层次结构并不意味着指定真正的具体 classes - 它意味着指定一个描述其他的接口classes 可能代表也可能不代表它。 (这与 collections.abc
中的 classes 不同——它描述了 'protocol' 和 还有 的混合方法实现)
这意味着当一个 做 将协议的元 class 和 wx.grid.Grid 结合起来时,得到的只是另一个错误:
In [2]: import wx.grid
In [3]: wx.grid.Grid.__class__
Out[3]: sip.wrappertype
In [4]: m1 = wx.grid.Grid.__class__
In [5]: from typing import Protocol
In [6]: m2 = Protocol.__class__
In [7]: class m3(m1, m2): pass
In [8]: class test(wx.grid.Grid, Protocol): pass
[...]
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
In [9]: class test(wx.grid.Grid, Protocol, metaclass=m3): pass
[...]
TypeError: Protocols can only inherit from other protocols, got <class 'wx._grid.Grid'>
因此,正确的做法是根本不继承协议 - 实现具有协议具体实现的 class,就像一个混入,并正常使用它来组成你的网格class - 还有你的
协议层次结构就像一组虚拟 classes,仅包含方法、属性及其注释 - 这将继承自 typing.Protocol
如果你不想写两次方法和注解的声明,这是可以理解的,可以声明实现协议的具体classes,并使用一些代码来运行 在继承自 Protocol
的 class 的 body 中。此代码可以将方法(及其注释)从其他 class 复制到您的协议(抽象)class - 然后它将与静态检查器和依赖静态注释的其他软件一起工作:
In [19]: class MyBase:
...: def foo(self) -> None: pass
...:
In [20]: class MyProtcol(Protocol):
...: for name in dir(MyBase):
...: if not name.startswith("__"):
...: locals()[name] = getattr(PBase, name)
...:
...:
In [21]: MyProtcol._is_protocol
Out[21]: True
我有一个显示各种网格的应用程序。网格具有不同种类的功能,所以我的设计是一个处理通用网格事物的基本网格 class,以及混合到具有基本网格的 class 中的各种功能混合:
class BaseGrid(wx.grid.Grid):
def foo():
return 0
class Grid_Mixin1():
def feature():
self.foo()
class Grid_Mixin2():
def feature():
self.foo()
class SpecificGrid(Mixin1, BaseGrid):
...
问题是我正在尝试使用类型提示,在 mixin 内部,类型检查器不知道 self.foo()
会存在,从而引发未知成员错误。
我决定使用协议让静态类型检查器知道 mixins 符合什么:
from typing import Protocol
class MyProtocol(Protocol):
def foo(self):
...
class Grid_Mixin1(MyProtocol):
def feature():
self.foo()
class Grid_Mixin2(MyProtocol):
def feature():
self.foo()
现在,当我尝试使用 SpecificGrid
时,我得到了臭名昭著的
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
我只能假设 wx.grid.Grid 是从它自己的元 class 派生出来的?从文档中看,这对我来说并不明显,但这是我唯一的解释。我的评估正确吗?我该怎么做才能解决这个问题?
是的 - 它们有不同的元class,因此您必须创建一个组合的元class才能将两个class结合起来。
这比听起来容易 - 因为 wx 和 typing 都是维护良好的代码,并且在 class 布局中没有内在冲突,创建一个不冲突的 metaclass 只是一个问题结合 metaclasses.
然而,问题在于 typing.Protocol subclasses:这个层次结构并不意味着指定真正的具体 classes - 它意味着指定一个描述其他的接口classes 可能代表也可能不代表它。 (这与 collections.abc
中的 classes 不同——它描述了 'protocol' 和 还有 的混合方法实现)
这意味着当一个 做 将协议的元 class 和 wx.grid.Grid 结合起来时,得到的只是另一个错误:
In [2]: import wx.grid
In [3]: wx.grid.Grid.__class__
Out[3]: sip.wrappertype
In [4]: m1 = wx.grid.Grid.__class__
In [5]: from typing import Protocol
In [6]: m2 = Protocol.__class__
In [7]: class m3(m1, m2): pass
In [8]: class test(wx.grid.Grid, Protocol): pass
[...]
TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
In [9]: class test(wx.grid.Grid, Protocol, metaclass=m3): pass
[...]
TypeError: Protocols can only inherit from other protocols, got <class 'wx._grid.Grid'>
因此,正确的做法是根本不继承协议 - 实现具有协议具体实现的 class,就像一个混入,并正常使用它来组成你的网格class - 还有你的
协议层次结构就像一组虚拟 classes,仅包含方法、属性及其注释 - 这将继承自 typing.Protocol
如果你不想写两次方法和注解的声明,这是可以理解的,可以声明实现协议的具体classes,并使用一些代码来运行 在继承自 Protocol
的 class 的 body 中。此代码可以将方法(及其注释)从其他 class 复制到您的协议(抽象)class - 然后它将与静态检查器和依赖静态注释的其他软件一起工作:
In [19]: class MyBase:
...: def foo(self) -> None: pass
...:
In [20]: class MyProtcol(Protocol):
...: for name in dir(MyBase):
...: if not name.startswith("__"):
...: locals()[name] = getattr(PBase, name)
...:
...:
In [21]: MyProtcol._is_protocol
Out[21]: True