【问题标题】:Is it possible to subclass wx.grid.Grid and also use my own metaclass?是否可以继承 wx.grid.Grid 并使用我自己的元类?
【发布时间】:2021-05-06 17:57:08
【问题描述】:

我有一个显示各种网格的应用程序。网格具有不同种类的功能,所以我的设计是一个处理通用网格事物的基本网格类,以及将各种特征混合到一个具有基本网格的类中:

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() 会存在,从而引发未知成员错误。

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

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 是从它自己的元类派生的?从文档中对我来说并不明显,但这是我唯一的解释。我的评估是否正确?我能做些什么来解决这个问题?

【问题讨论】:

    标签: python-3.x wxpython protocols static-analysis metaclass


    【解决方案1】:

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

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

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

    这意味着当一个确实结合了 Protocol 和 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'>
    

    因此,正确的做法是根本不从 Protocol 继承 - 实现具有协议具体实现的类,就像 mixin 一样,并正常使用它来组成 Grid 类 - 并让你的 协议层次结构就像一组虚拟类,仅包含方法、属性及其注释 - 这将继承自 typing.Protocol

    如果你不想把方法和注解的声明写两次,这是可以理解的,可以声明实现协议的具体类,并在body中使用一些代码来运行

    em> 继承自 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
    

    【讨论】:

      猜你喜欢
      • 2016-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-22
      • 2014-02-13
      • 1970-01-01
      • 2016-03-14
      • 1970-01-01
      相关资源
      最近更新 更多