简体   繁体   English

在已更新的 Counter() 对象上使用 pprint 的 TypeError(边缘情况的位)

[英]TypeError using pprint on Counter() objects that have been updated (bit of an edge case)

Under some circumstances, Python pretty print (pprint.pprint) produces a TypeError, and it caught me a bit by surprise.在某些情况下,Python pretty print (pprint.pprint) 会产生 TypeError,这让我有点意外。

We can create a Counter object from (eg) a list of integers and pretty print it:我们可以从(例如)整数列表创建一个计数器 object 并漂亮地打印它:

from collections import Counter
from pprint import pprint

intlist = [1,2,3,4,5,6,5,2,5,9,4,7,2,1,4,6,8,54,6,2,45,6,8,4,21,23,6,7,3,35561,1,6,8,]
intcounter = Counter(intlist)
pprint(intcounter)

Counter({6: 6, 2: 4, 4: 4, 1: 3, 5: 3, 8: 3, 3: 2, 7: 2, 9: 1, 54: 1, 45: 1, 21: 1, 23: 1, 35561: 1})计数器({6:6、2:4、4:4、1:3、5:3、8:3、3:2、7:2、9:1、54:1、45:1、21:1 , 23: 1, 35561: 1})

We can add a key to it without converting it to a "native" dictionary too (because Counters are a subclass of dict)我们可以在不将其转换为“本机”字典的情况下为其添加一个键(因为 Counters 是 dict 的子类)

from collections import Counter
from pprint import pprint

intlist = [1,2,3,4,5,6,5,2,5,9,4,7,2,1,4,6,8,54,6,2,45,6,8,4,21,23,6,7,3,35561,1,6,8,]
intcounter = Counter(intlist)
intcounter["Hello"] = "World"
# and you can print that too
print(intcounter)

Counter({1: 3, 2: 4, 3: 2, 4: 4, 5: 3, 6: 6, 9: 1, 7: 2, 8: 3, 54: 1, 45: 1, 21: 1, 23: 1, 35561: 1, 'Hello': 'World'})计数器({1:3、2:4、3:2、4:4、5:3、6:6、9:1、7:2、8:3、54:1、45:1、21:1 , 23: 1, 35561: 1, '你好': '世界'})

but can we then prettyprint the updated object?但是我们可以漂亮地打印更新的 object 吗?

try:
    pprint(intcounter)
except Exception as t:
    print(t)

Nope.没有。

Counter({'<' not supported between instances of 'int' and 'str' Counter({'<' 在 'int' 和 'str' 的实例之间不支持

Ok how about we turn pprint's default sorting behaviour off?好的,我们关闭 pprint 的默认排序行为怎么样?

try:
    pprint(intcounter, sort_dicts=False)
except TypeError as t:
    print(t)

also nope:也不行:

Counter({'<' not supported between instances of 'int' and 'str' Counter({'<' 在 'int' 和 'str' 的实例之间不支持

Note also that we can't use update on a Counter() object if a value in the updating dict is type str (even though, as above, we can add the key:value "directly")另请注意,如果更新字典中的值是 str 类型,我们不能在 Counter() object 上使用更新(尽管如上所述,我们可以“直接”添加键:值)

try:
    intcounter.update({"Hello": "World"})
except TypeError as t:
    print(t)

can only concatenate str (not "int") to str只能将 str(不是“int”)连接到 str

I think (but I'm just hamfisted amateur coder so I'm not sure) that the Python docs for Counter() might cover why we can't use the update method:我认为(但我只是笨拙的业余编码员,所以我不确定) Counter() 的 Python 文档可能涵盖了为什么我们不能使用更新方法:

Note Counters were primarily designed to work with positive integers to represent running counts;注意 计数器主要设计用于使用正整数来表示运行计数; however, care was taken to not unnecessarily preclude use cases needing other types or negative values.但是,注意不要不必要地排除需要其他类型或负值的用例。 To help with those use cases, this section documents the minimum range and type restrictions.为了帮助解决这些用例,本节记录了最小范围和类型限制。 The Counter class itself is a dictionary subclass with no restrictions on its keys and values.计数器 class 本身是一个字典子类,对其键和值没有限制。 The values are intended to be numbers representing counts, but you could store anything in the value field.这些值旨在是表示计数的数字,但您可以在值字段中存储任何内容。

The most_common() method requires only that the values be orderable. most_common() 方法只要求值是可排序的。

For in-place operations such as c[key] += 1, the value type need only support addition and subtraction.对于 c[key] += 1 等就地操作,值类型只需要支持加法和减法即可。 So fractions, floats, and decimals would work and negative values are supported.所以分数、浮点数和小数都可以使用,并且支持负值。 The same is also true for update() and subtract() which allow negative and zero values for both inputs and outputs. update() 和subtract() 也是如此,它们允许输入和输出都为负值和零值。

The multiset methods are designed only for use cases with positive values.多集方法仅适用于具有正值的用例。 The inputs may be negative or zero, but only outputs with positive values are created.输入可能是负数或零,但只会创建具有正值的输出。 There are no type restrictions, but the value type needs to support addition, subtraction, and comparison.没有类型限制,但是值类型需要支持加减比较。

The elements() method requires integer counts. elements() 方法需要 integer 计数。 It ignores zero and negative counts.它忽略零计数和负计数。

Obviously if we force the Counter object to a "native" dictionary ( dict(intcounter) ) everything will work as expected, but I wondered if pprint should handle this a bit more elegantly, even though I realise this is quite edge-casey and very few people will trip over this in the same way I did.显然,如果我们将 Counter object 强制为“本机”字典( dict(intcounter) ),一切都会按预期工作,但我想知道 pprint 是否应该更优雅地处理这个问题,即使我意识到这是非常前沿的,非常很少有人会像我一样绊倒这个。

(I was passing a Counter() to a bokeh charting function & it seemed convenient to pass some extra k:v pairs that the function used by simply updating the Counter() object, pprint was just used to visually check my work) (我将 Counter() 传递给散景图 function 并且传递一些额外的 k:v 对似乎很方便,function 使用的 function 通过简单地更新 Counter() ZA8CFDE6331BD59EB62AC96F8911 来直观地检查 B 我的作品),

Python 3.8 btw. Python 3.8 顺便说一句。

pprint is not at blame here. pprint不是这里的罪魁祸首。 When you perform your call:当您执行呼叫时:

pprint(intcounter)

This will actually call __repr__ from Countrer Which is the one calling most_common这实际上会从Countrer调用__repr__调用most_common

def __repr__(self):
    if not self:
        return f'{self.__class__.__name__}()'
    try:
        # dict() preserves the ordering returned by most_common()
        d = dict(self.most_common())
    except TypeError:
        # handle case where values are not orderable
        d = dict(self)
    return f'{self.__class__.__name__}({d!r})'

Note that when you add your key/value, either by assignment ( [key] = value ) or using update they are not validated.请注意,当您通过分配( [key] = value )或使用更新添加键/值时,它们不会被验证。

The class Counter assume that you pass the value as type int but does no such validation for it. class 计数器假定您将值作为int类型传递,但没有对其进行此类验证。

When you using update, the code won't validate it either but will crash at line:当您使用更新时,代码也不会验证它,但会在行崩溃:

self[elem] = count + self_get(elem, 0)

Since count is the value you passed of type str and it cannot be concatenate with 0 .由于count是您传递的str类型的值,它不能与0连接。

As opposed to using assignment, where the line is basically:与使用分配相反,该行基本上是:

self[key] = value

The update method will concatenate the previous value with the new value. update 方法会将之前的值与新的值连接起来。 So basically if the value was 5 and you add 1, the result would be 6. In the case you assigned a str value it will raise an unhandled exception.因此,基本上如果值为 5 并且您添加 1,则结果将为 6。如果您分配了str值,它将引发未处理的异常。

Now again this will pass using assigment, but once any methods must do computation it will eventually crash.现在这将再次使用分配传递,但是一旦任何方法必须进行计算,它最终会崩溃。

Always ensure your value when using counter is of type int使用 counter 时始终确保您的值是int类型

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM