如何处理名称冲突 collections.Counter 和 typing.Counter?

Posted

技术标签:

【中文标题】如何处理名称冲突 collections.Counter 和 typing.Counter?【英文标题】:How to deal with name *** collections.Counter and typing.Counter? 【发布时间】:2020-08-16 10:49:47 【问题描述】:

名称Countercollections(作为类)和typing(作为通用类型名称)中都定义了。不幸的是,它们略有不同。处理此问题的推荐方法是什么?

异同:

    from collections import Counter之后,

      您可以调用构造函数Counter("foo") 来创建一个新的Counter 对象; 你可以验证它是dict的子类:issubclass(Counter, dict)返回True; 您不能使用它来声明Counter 的特定变体,例如cnt: Counter[str] = Counter("foo") raises TypeError: 'type' object is not subscriptable(类型提示失败)

    from typing import Counter之后,

      您可以调用构造函数Counter("foo") 来创建一个新的Counter 对象(实际上,有点令我惊讶); 你不能用它来验证它是dict的子类:issubclass(Counter, dict) raises TypeError: issubclass() arg 1 must be a class; 您可以声明Counter 的特定变体,例如cnt: Counter[str] = Counter("foo")

在很多情况下 1.1 和 2.1 已经足够好了,所以导入的选择并不重要。 但似乎您不能同时使用 1.3 和 2.2 进行一次导入。后两者中,类型提示比子类检查更重要。 如果你想写类型提示,那么from typing import Counter 就足够了。 不过,如果您编写,我会发现它更清晰(并且更符合某些其他类型的需求)

from collections import Counter  # to indicate that you want the implementation
from typing import Counter  # to indicate that you want to write type hints

(注意顺序很重要。)

如果你想拥有这一切怎么办? 这些是我看到的选项:

from collections import Counter
import typing

并使用typing.Counter 实现1.3。不好听,太啰嗦了。

import collections
from typing import Counter

并使用collections.Counter 实现2.2(如果需要;我在教学中需要它)。

from collections import Counter as counter
from typing import Counter

并使用counter实现2.2。

from collections import Counter
from typing import Counter as Bag  # or Multiset

并在类型提示中使用Bag(或Multiset)。 (但这肯定会令人困惑。)

    做(按照评论中的建议)
import collections as co  # to access the class
from typing import Counter  # to access constructor and provide type hints

并使用

    co.CounterCounter 作为构造函数 使用co.Counter 作为类,例如issubclass(co.Counter, dict) 在类型提示中使用Counter,例如cnt: Counter[str]

那么是不是也值得推荐呢

from typing import Deque

并使用Deque 作为构造函数,而不是co.deque? (我认为/希望不会。)

对于其他类型(例如defaultdictdeque),这似乎不是问题:

from collections import defaultdict, deque
from typing import DefaultDict, Deque

给你所有。

我是否忽略了什么?

【问题讨论】:

您可以添加另一个:import collections as co; c = co.Counter('this is a good question') 为什么不使用完全导入? typing.Counter 和 Collections.Counter 很容易区分并提供更好的可读性。 @MortenB 我在教育环境中需要这个,与刚开始学习编程的学生一起。这似乎是一种不必要的复杂化,尤其是因为其他类型不需要这种方法。 【参考方案1】:

可能有一些更高级的情况这不起作用,但您可以创建一个继承自两者的子类:

import typing
import collections

KT = typing.TypeVar("KT")

class Counter(collections.Counter, typing.Counter[KT]): pass

c: Counter[str] = Counter("foo")

print(isinstance(c, dict))  # True

【讨论】:

但是你不想把这个扔给初级程序员,因为其他类型的东西不是问题。 @TomVerhoeff 如果我正在设计它,我根本不会有单独的 typing 类,但我想有一些令人信服的技术原因是不可能的。【参考方案2】:

最小的痛苦可能是满足于基于字符串的类型提示。然而,并不是每个 IDE 都会解释它。 PyCharm 在这个 sn-p 的最后一行检测到类型不匹配,而 VSCode 认为一切正常。

from collections import Counter

mycounter: 'Counter[str]' = Counter('foo')

def func1(counter: 'Counter[str]'):
    pass

def func2(counter: 'Counter[int]'):
    pass

func1(mycounter)
func2(mycounter)

【讨论】:

当然是个好建议,这将使您有一个统一的方法。但它相当普遍,不是主流,在教学中它会让人觉得做作和不必要。【参考方案3】:

从 Python 3.9 开始,您可以:

from collections import Counter

c: Counter[str] = Counter()

见:https://docs.python.org/3/library/typing.html#typing.Counter

3.9 版后已弃用:collections.Counter 现在支持 []。请参阅 PEP 585 和通用别名类型。

【讨论】:

以上是关于如何处理名称冲突 collections.Counter 和 typing.Counter?的主要内容,如果未能解决你的问题,请参考以下文章

HashTables 如何处理冲突?

如何处理访问写入冲突错误? [复制]

如何处理列名('hash')与 Rails 冲突的旧数据库表?

Django slugify url - 如何处理冲突?

咨询师如何处理冲突?

在 VC++ 中获取读取访问冲突异常如何处理此异常?