如何在没有元类冲突的情况下将泛型类型与 PyQt 子类一起使用?
问题描述
我曾尝试使用 sip 包装器类型的 abc.ABCMeta
,它在使用 abc.ABC
进行子类化时效果很好.
I had tried the abc.ABCMeta
with sip wrapper type, and it works well when subclass with abc.ABC
.
class QABCMeta(wrappertype, ABCMeta):
pass
class WidgetBase(QWidget, metaclass=QABCMeta):
...
class InterfaceWidget(WidgetBase, ABC):
...
class MainWidget(InterfaceWidget):
...
但它不适用于 typing.Generic
.
class QGenericMeta(wrappertype, GenericMeta):
pass
class WidgetBase(QWidget, Generic[T], metaclass=QGenericMeta):
...
class GenericWidget(WidgetBase[float]):
...
它提出了:
line 980, in __new__
self if not origin else origin._gorg)
TypeError: can't apply this __setattr__ to sip.wrappertype object
我希望它像往常一样使用通用子类:
I expected it to use generic subclass as usual:
class TableBase(QTableWidget, Generic[T]):
@abstractmethod
def raw_item(self, row: int) -> T:
...
def data(self) -> Iterator[T]:
yield from (self.raw_item(row) for row in range(self.rowCount()))
class MainTable(TableBase[float]):
def raw_item(self, row: int) -> float:
return float(self.item(row, 1).text()) # implementation
table = MainTable()
for data in table.data():
data: float
但是在没有继承Generic[T]
的情况下,data
仍然是Any
.
But the data
is still Any
when without inherit Generic[T]
.
可以用 PEP 560 解决类型检查吗?
Can it solved with PEP 560 to do type checking?
解决方案
嗯,我找到了答案.
由于typing.Generic
的元类是abc.ABC
,它也应该基于abc.ABCMeta
.但这仅适用于 Python 3.7 或更高版本.
Since the metaclass of typing.Generic
is abc.ABC
, it should based on abc.ABCMeta
too. But this is only works with Python 3.7 or above.
然后,只需使用 type(QObject)
而不是 sip.wrappertype
:
And then, just use type(QObject)
instead of sip.wrappertype
:
# -*- coding: utf-8 -*-
from abc import abstractmethod, ABC, ABCMeta
from typing import TypeVar, Generic, Iterator
from PyQt5.QtCore import QObject
from PyQt5.QtWidgets import QTableWidget
QObjectType = type(QObject)
T = TypeVar('T')
class QABCMeta(QObjectType, ABCMeta):
pass
class BaseWidget(QTableWidget, Generic[T], metaclass=QABCMeta):
@abstractmethod
def raw_item(self, row: int) -> T:
...
def data(self) -> Iterator[T]:
yield from (self.raw_item(row) for row in range(self.rowCount()))
class TestWidget(BaseWidget[float], ABC): # optional inherit ABC.
def raw_item(self, row: int) -> float:
return float(self.item(row, 1).text())
if __name__ == '__main__':
w = TestWidget()
for f in w.data():
pass
此代码适用于PyCharm IDE,变量f
的注解为float
.
This code is works for PyCharm IDE, the annotation of variable f
is float
.
把PyQt5
改成PySide2
也可以!
相关文章