【问题标题】:What's the underlying implementation for most_common method of Counter?Counter 的 most_common 方法的底层实现是什么?
【发布时间】:2019-04-16 19:50:06
【问题描述】:

我找到了一个 pyi 文件,它的定义如下

def most_common(self, n: Optional[int] = ...) -> List[Tuple[_T, int]]: ...

怎么会这样?列表没有定义,也没有实现?


在这里为关注者突出一些有价值的建议:

列表是从打字模块导入的;它与列表不同。 .pyi 文件不需要导入它,因为存根文件永远不会被执行;它们只需要在语法上是有效的 Python

如果您使用 from future 导入注释,则无需导入类型即可使用 List 等。在 .py 文件中的函数注释中也是如此,因为函数注释将被视为字符串文字。 (从 Python 4 开始,这将是默认行为。有关详细信息,请参阅 PEP 563。)

【问题讨论】:

  • 怎么会这样? Python 本身不使用存根文件,只有像 mypy 这样的静态检查器。

标签: python-3.x counter pyi


【解决方案1】:

您正在查看仅用于注释的pyi 文件。它永远不会由 Python 解释器执行。您可以通过阅读 PEP484 了解有关 pyi 文件的更多信息。

使用调试器,在调用most_common的行上放置一个断点,然后单步执行该方法。

Python 3.7 实现。

...\Lib\collections\__init__.py:

def most_common(self, n=None):
    '''List the n most common elements and their counts from the most
    common to the least.  If n is None, then list all element counts.

    >>> Counter('abcdeabcdabcaba').most_common(3)
    [('a', 5), ('b', 4), ('c', 3)]

    '''
    # Emulate Bag.sortedByCount from Smalltalk
    if n is None:
        return sorted(self.items(), key=_itemgetter(1), reverse=True)
    return _heapq.nlargest(n, self.items(), key=_itemgetter(1))

_heapq.nlargest(在...\Lib\heapq.py)实现:

def nlargest(n, iterable, key=None):
    """Find the n largest elements in a dataset.

    Equivalent to:  sorted(iterable, key=key, reverse=True)[:n]
    """

    # Short-cut for n==1 is to use max()
    if n == 1:
        it = iter(iterable)
        sentinel = object()
        if key is None:
            result = max(it, default=sentinel)
        else:
            result = max(it, default=sentinel, key=key)
        return [] if result is sentinel else [result]

    # When n>=size, it's faster to use sorted()
    try:
        size = len(iterable)
    except (TypeError, AttributeError):
        pass
    else:
        if n >= size:
            return sorted(iterable, key=key, reverse=True)[:n]

    # When key is none, use simpler decoration
    if key is None:
        it = iter(iterable)
        result = [(elem, i) for i, elem in zip(range(0, -n, -1), it)]
        if not result:
            return result
        heapify(result)
        top = result[0][0]
        order = -n
        _heapreplace = heapreplace
        for elem in it:
            if top < elem:
                _heapreplace(result, (elem, order))
                top, _order = result[0]
                order -= 1
        result.sort(reverse=True)
        return [elem for (elem, order) in result]

    # General case, slowest method
    it = iter(iterable)
    result = [(key(elem), i, elem) for i, elem in zip(range(0, -n, -1), it)]
    if not result:
        return result
    heapify(result)
    top = result[0][0]
    order = -n
    _heapreplace = heapreplace
    for elem in it:
        k = key(elem)
        if top < k:
            _heapreplace(result, (k, order, elem))
            top, _order, _elem = result[0]
            order -= 1
    result.sort(reverse=True)
    return [elem for (k, order, elem) in result]

【讨论】:

  • @Pythoner 我在 Pycharm 中使用了调试器。在调用most_common 的行上放置一个断点,然后单步执行该方法。
  • @Pythoner 存根文件旨在与github.com/python/cpython/blob/3.7/Lib/collections/__init__.py结合使用。
  • @DeepSpace,非常好的和详细的anwser,非常感谢!
  • List 是从typing 模块导入的;它与list 不同。 .pyi 文件不需要导入它,因为存根文件永远不会执行;它们必须是语法有效的 Python。
  • 如果您使用from __future__ import annotations,则无需导入typing 即可使用List 等。在 .py 文件中的函数注释中,因为函数注释将被视为字符串文字。 (从 Python 4 开始,这将是默认行为。有关详细信息,请参阅 PEP 563。)
猜你喜欢
  • 1970-01-01
  • 2012-04-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多