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

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

异同:

  1. from collections import Counter之后,

    1. 您可以调用构造函数Counter("foo") 来创建一个新的Counter 对象;
    2. 你可以验证它是dict的子类:issubclass(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

(注意顺序很重要。)

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

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)。 (但这肯定会令人困惑。)

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

并使用

  1. co.CounterCounter 作为构造函数
  2. 使用co.Counter 作为类,例如issubclass(co.Counter, dict)
  3. 在类型提示中使用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 我在教育环境中需要这个,与刚开始学习编程的学生一起。这似乎是一种不必要的复杂化,尤其是因为其他类型不需要这种方法。

标签: python collections counter type-hinting


【解决方案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 和通用别名类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-02-16
    • 2021-08-14
    • 1970-01-01
    • 1970-01-01
    • 2019-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多