我已经尝试过使用 sip 包装类型,并且当使用
abc.ABCMeta
进行子类化时效果很好。
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
但是
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
仍然是
data
当没有继承
Any
时。
Generic[T]
可以用PEP 560解决进行类型检查吗?
Can it solved with PEP 560 to do type checking?
你碰到的问题是因为
typing.Generic
使用了元类来修改类的行为,并支持泛型类型。而 PyQt 使用
sip.wrappertype
来包装 C++ 类,这两种机制之间存在冲突。
以下是一种解决方法,可以在不使用元类的情况下将泛型类型与 PyQt 子类一起使用,并利用 PEP 560 进行类型检查:
from typing import TypeVar, Generic, Iterator, List
from PyQt5.QtWidgets import QWidget, QTableWidget
T = TypeVar('T')
class WidgetBase(QWidget):
"""基础 Widget 类,不使用泛型"""
pass
class TableBase(QTableWidget, Generic[T]):
def raw_item(self, row: int) -> T:
raise NotImplementedError
def data(self) -> Iterator[T]:
for row in range(self.rowCount()):
yield self.raw_item(row)
class FloatTable(TableBase[float]):
def raw_item(self, row: int) -> float:
return float(self.item(row, 1).text())
class StringTable(TableBase[str]):
def raw_item(self, row: int) -> str:
return self.item(row, 0).text()
# 使用示例
float_table = FloatTable()
for data in float_table.data():
print(f"Data: {data}, Type: {type(data)}") # data 的类型为 float
string_table = StringTable()
for data in string_table.data():
print(f"Data: {data}, Type: {type(data)}") # data 的类型为 str
解释:
-
不使用元类:
我们不再尝试创建自定义元类来组合
sip.wrappertype
和typing.Generic
。 -
直接继承
Generic
: 让TableBase
直接继承typing.Generic[T]
,从而获得泛型支持。 -
类型提示:
使用类型提示 (
-> T
) 来指定raw_item
和data
方法的返回类型。 -
具体实现类:
创建具体的子类,如
FloatTable
和StringTable
,并实现raw_item
方法以返回特定类型的数据。
优点:
- 避免了元类冲突。
- 代码更简洁易懂。
- PEP 560 仍然可以对类型进行检查,因为我们使用了类型提示。
缺点:
-
WidgetBase
类本身不能是泛型的。
这种方法提供了一种更简单的方式来使用泛型类型和 PyQt,同时仍然可以利用类型提示进行类型检查。
标签:python,pyqt5,python-typing,pyside2,metaclass From: 54635205