是否有可能子类化wx.grid.Grid,并使用我自己的元类?



我有一个显示各种网格的应用程序。网格有不同种类的功能,所以我的设计是一个基本网格类,处理一般的网格事物,和各种功能混合到一个类与基本网格:

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):
...

问题是我试图使用类型提示,并且在mixins中,类型检查器不知道self.foo()将存在,引发未知成员错误。

我决定使用协议来让静态类型检查器知道混合符合什么:

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:元类冲突:派生类的元类必须是其所有基类的元类的(非严格)子类

我只能假设wx.grid.Grid是从它自己的元类派生的?从文档中我看不出来,但这是我唯一的解释。我的评估正确吗?我该怎么做才能避开这个问题呢?

是的-它们有不同的元类,因此您必须创建一个组合元类才能组合这两个类。

这比听起来容易——因为wx和typing都是维护良好的代码,并且在类布局中没有固有的冲突,因此创建一个无冲突的元类只需要组合两个元类。

然而,问题在于打字。协议子类:这个层次结构并不是要指定真正的具体类——它是要指定一个接口,用来描述其他可能代表也可能不代表它的类。(这与collections.abc中的类不同-它描述了一个"协议"和有mixin方法的实现)

这意味着当一个组合Protocol和wx.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'>

所以,正确的做法是根本不继承协议——实现你的类,实现你的协议的具体实现,就像一个mixin,并使用它来组成你的网格类——并拥有你的协议层次结构就像一组虚拟类,只包含方法、属性和它们的注释——这将继承自typing.Protocol

如果你不想写两次方法和注释的声明,这是可以理解的,可以声明实现协议的具体类,并使用一些代码在继承Protocol的类的中运行。这段代码可以将方法(及其注释)从另一个类复制到您的协议(抽象)类中——然后它将与静态检查器和其他依赖于静态注释的软件一起工作:

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

相关内容

最新更新