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

wst*_*omv 5 python collections counter type-hinting

该名称Countercollections(作为类)和在typing(作为泛型类型名称)中定义。不幸的是,它们略有不同。处理这个问题的推荐方法是什么?

相同点和不同点:

  1. 之后from collections import Counter

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

    1. 你可以调用构造函数Counter("foo")来创建一个新的Counter对象(实际上,有点出乎我的意料);
    2. 你不能用它来验证它是dict: issubclass(Counter, dict)raises 的子类TypeError: issubclass() arg 1 must be a class
    3. 您可以声明 的特定变体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
Run Code Online (Sandbox Code Playgroud)

(注意顺序很重要。)

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

from collections import Counter
import typing
Run Code Online (Sandbox Code Playgroud)

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

import collections
from typing import Counter
Run Code Online (Sandbox Code Playgroud)

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

from collections import Counter as counter
from typing import Counter
Run Code Online (Sandbox Code Playgroud)

并使用counter实现2.2。

from collections import Counter
from typing import Counter as Bag  # or Multiset
Run Code Online (Sandbox Code Playgroud)

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

  1. 做(如评论中所建议)
import collections as co  # to access the class
from typing import Counter  # to access constructor and provide type hints
Run Code Online (Sandbox Code Playgroud)

并使用

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

那么是不是也被推荐做

from typing import Deque
Run Code Online (Sandbox Code Playgroud)

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

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

from collections import defaultdict, deque
from typing import DefaultDict, Deque
Run Code Online (Sandbox Code Playgroud)

给你所有。

我是否忽略了什么?

Jea*_*net 5

从 Python 3.9 开始,您可以执行以下操作:

from collections import Counter

c: Counter[str] = Counter()
Run Code Online (Sandbox Code Playgroud)

请参阅:https://docs.python.org/3/library/typing.html#typing.Counter

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


Pat*_*ugh 0

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

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
Run Code Online (Sandbox Code Playgroud)