Подсказка типа для комбинированного класса и подкласса Mix-in приводит к ошибкам типа.Python

Программы на Python
Ответить
Anonymous
 Подсказка типа для комбинированного класса и подкласса Mix-in приводит к ошибкам типа.

Сообщение Anonymous »

Следующий фрагмент кода объединяет некоторые классы данных и классы GUI с использованием PySide6 (библиотека Qt).
Класс HasDataobject играет здесь ключевую роль. Он определяет примесь для подклассов QGraphicsItem. Он добавляет атрибут (

Код: Выделить всё

dataobject
) и метод (

Код: Выделить всё

update_visibility()
) к этим классам.

Код: Выделить всё

from typing import TypeVar, Generic, Protocol

from PySide6.QtWidgets import QGraphicsItem, QGraphicsEllipseItem
# QGraphicsEllipseItem is a subclass of QGraphicsItem

### Data objects ###

class VisibleData:
def is_visible(self) -> bool:
return True

class MyNode(VisibleData):
pass

VisibleDataType = TypeVar('VisibleDataType', bound=VisibleData)

### Visual objects (using PySide6) ###

class QGraphicsItemProtocol(Protocol):
"""Define the methods of QGraphicsItem that HasDataobject uses."""
def setVisible(self, visible: bool, /):
...

class HasDataobject(Generic[VisibleDataType]):
"""Mix-in class. Adds an update_visibility() method, and
a dataobject attribute. The dataobject must have a
is_visible() method, as defined in VisibleData.
Any subclass of HasDataobject must also be a subclass of
QGraphicsItem, which defines setVisible()."""
dataobject: VisibleDataType
def update_visibility(self):
self.setVisible(self.dataobject.is_visible())

class Circle(QGraphicsEllipseItem, HasDataobject[MyNode]):
def __init__(self):
super().__init__()
pass
Приведенный выше код работает без проблем. Однако Pyright или другие валидаторы кода будут жаловаться, что setVisible не является известным методом (reportAttributeAccessIssue):

Код: Выделить всё

self.setVisible(self.dataobject.is_visible())
Самый простой способ подавить это — добавить # type: ignore, но я предпочитаю явно указывать то, что описано в строке документации: Любой подкласс HasDataobject также должен быть подклассом QGraphicsItem.
Моя первоначальная мысль заключалась в том, чтобы сделать HasDataobject подклассом QGraphicsItem:

Код: Выделить всё

class HasDataobject(QGraphicsItem, Generic[VisibleDataType])
Однако это приводит к следующему методу RuntimeError в Circle.__init__:

RuntimeError: невозможно инициализировать объект PySide6.QtWidgets.QGraphicsEllipseItem в классе Circle дважды!

Поэтому моя вторая попытка — использовать метод QGraphicsItemProtocol определяется выше:

Код: Выделить всё

class HasDataobject(Generic[VisibleDataType], QGraphicsItemProtocol)
Однако это дает

TypeError: невозможно создать согласованный порядок разрешения методов (MRO) для баз Generic, QGraphicsItemProtocol

Поэтому затем я попытался поменять местами два базовых класса:

Код: Выделить всё

class HasDataobject(QGraphicsItemProtocol, Generic[VisibleDataType])
Однако это снова приводит к проблеме при определении класса Circle:

TypeError: конфликт метаклассов: метакласс производного класса должен быть (нестрогим) подклассом метаклассов всех его баз.

Я немного застрял. Как я могу использовать подсказки типов в этом коде и осчастливить Пайрайта (и себя)?
PS: Любые предложения и лучшие практики приветствуются. Я даже подумывал сделать HasDataobject настоящим классом (has-a QGraphicsItem вместо is-a QGraphicsItem) вместо примеси, но создание подклассов действительно полезно, поскольку оно позволяет использовать возможности Qt с такими вещами, как Scene.addItem(a_circle).

Подробнее здесь: https://stackoverflow.com/questions/797 ... typeerrors
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»