如何在通用Python类中输入__eq__?

问题描述 投票:0回答:1

当我定义带有泛型类型参数的类型时(例如映射的键和值的

K
/
V
),我似乎无法编写合适的
isinstance
检查来实现
__eq__

from collections import OrderedDict
from collections.abc import MutableMapping

K = TypeVar("K")
V = TypeVar("V")

class MyDictWrapper(MutableMapping[K, V]):

  def __init__(self):
    self.cache: OrderedDict[K, V] = OrderedDict()

  def __eq__(self, other: object) -> bool:
    if not isinstance(other, LRUCache):
      return NotImplemented
    return self.cache == other.cache  # Pyright error: Type of "cache" is partially unknown: Type of "cache" is "OrderedDict[Unknown, Unknown]

替代方案

    if not isinstance(other, LRUCache[K, V]):

也不起作用,错误为:

Second argument to "isinstance" must be a class or tuple of classes
  Generic type with type arguments not allowed for instance or class 

有正确的输入方法吗?

python mypy python-typing pyright
1个回答
0
投票

您遇到的问题源于 Python 的运行时类型检查限制,尤其是泛型。 Python 的 isinstance 函数不支持直接检查泛型类型及其参数(例如,LRUCache[K, V])。此限制是因为由于 Python 的类型擦除,泛型类型参数(在您的情况下为 K、V)不会在运行时保留。

要以与类型检查兼容的方式正确实现 __eq__ 方法,同时在运行时仍然实用,您可以使用尊重 Python 类型系统动态特性的方法组合。

您可以采取以下方法:

避免在 isinstance 中进行直接泛型类型检查:由于不能直接将 isinstance 与泛型类型一起使用,因此请检查不带泛型参数的基类。

间接确保运行时类型兼容性:使用其他运行时检查来确保对象兼容,重点关注方法所需的结构或行为,而不是精确的类型。

对静态类型检查器使用类型提示:虽然您无法在运行时强制执行泛型类型参数,但您仍然可以通过 Mypy 或 Pyright 等工具将它们用于静态类型分析。

鉴于您的代码示例,使用 LRUCache 而不是 MyDictWrapper 似乎存在错误。假设 MyDictWrapper 是正确的类名,这是 __eq__ 方法的调整版本:

从集合导入 OrderedDict 从 collections.abc 导入 MutableMapping 从输入 import TypeVar, Generic

K = TypeVar("K") V = TypeVar("V")

类 MyDictWrapper(MutableMapping, Generic[K, V]):

def __init__(self):
    self.cache: OrderedDict[K, V] = OrderedDict()

def __eq__(self, other: object) -> bool:
    # Check if 'other' is instance of MyDictWrapper without generic parameters
    if not isinstance(other, MyDictWrapper):
        return False
    # Since direct comparison of generic types at runtime is not feasible,
    # compare the underlying cache directly as a practical solution.
    return self.cache == other.cache

def __getitem__(self, k: K) -> V:
    return self.cache[k]

def __setitem__(self, k: K, v: V) -> None:
    self.cache[k] = v

def __delitem__(self, k: K) -> None:
    del self.cache[k]

def __iter__(self):
    return iter(self.cache)

def __len__(self) -> int:
    return len(self.cache)
© www.soinside.com 2019 - 2024. All rights reserved.