类型错误:* 后的 function() 参数必须是序列,而不是生成器 [英] TypeError: function() argument after * must be a sequence, not generator

查看:26
本文介绍了类型错误:* 后的 function() 参数必须是序列,而不是生成器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在尝试编写一个很小的、经过混淆的类型检查器时,发现了一个不可接受的代码模式.但是,它不一致地无法正常工作.这是最初编写用于测试的代码.

def statictypes(a):def b(a, b, c):如果 b 在 a 而不是 isinstance(c, a[b]): raise TypeError('{} 应该是 {}, not {}'.format(b, a[b], type(c)))返回 creturn __import__('functools').wraps(a)(lambda *c: b(a.__annotations__, 'return', a(*(b(a.__annotations__, *d) for d in zip(a.__code__.co_varnames), C)))))@静态类型def isallinstance(iterable: object, class_or_type_or_tuple: (type, tuple)) ->布尔:"""isallinstance(iterable, class_or_type_or_tuple) -> bool返回迭代中的所有项目是类的实例还是类的实例其子类.使用类型作为第二个参数,返回是否是所有项目的类型.使用元组的形式, isallinstance(x, (A, B, ...)),是 any(isallinstance(x, y) for y in (A, B, ...)) 的快捷方式."""返回所有(isinstance(item, class_or_type_or_tuple) for item in iterable)

以下显示了与 Python 解释器的对话,并突出显示了出现的错误.生成了 TypeError,但不是预期的.虽然生成器很好,但现在它们失败了.

<预><代码>>>>isallinstance(范围(1000000),int)真的>>>isallinstance(range(1000000), (int, float))真的>>>isallinstance(range(1000000), [int, float])回溯(最近一次调用最后一次):文件<pyshell#26>",第 1 行,在 <module> 中isallinstance(range(1000000), [int, float])文件C:\Users\schappell\Downloads\test.py",第 5 行,在 <lambda> 中.return __import__('functools').wraps(a)(lambda *c: b(a.__annotations__, 'return', a(*(b(a.__annotations__, *d) for d in zip(a.__code__.co_varnames), C)))))类型错误:* 后的 isallinstance() 参数必须是序列,而不是生成器

statictypes 函数可以重写,isallinstance 函数重新定义和包装.最简单的解决方案是将 statictypes 中的生成器重写为列表推导式.

def statictypes(a):def b(a, b, c):如果 b 在 a 而不是 isinstance(c, a[b]): raise TypeError('{} 应该是 {}, not {}'.format(b, a[b], type(c)))返回 creturn __import__('functools').wraps(a)(lambda *c: b(a.__annotations__, 'return', a(*[b(a.__annotations__, *d) for d in zip(a.__code__.co_varnames), C)])))

之后,isallinstance 一旦从头开始重新创建,就会按预期开始工作.说明第二个参数有什么问题的 TypeError 按预期正确生成.

<预><代码>>>>isallinstance(范围(1000000),int)真的>>>isallinstance(range(1000000), (int, float))真的>>>isallinstance(range(1000000), [int, float])回溯(最近一次调用最后一次):文件<pyshell#29>",第 1 行,在 <module> 中isallinstance(range(1000000), [int, float])文件C:\Users\schappell\Downloads\test.py",第 5 行,在 <lambda> 中.return __import__('functools').wraps(a)(lambda *c: b(a.__annotations__, 'return', a(*[b(a.__annotations__, *d) for d in zip(a.__code__.co_varnames), C)]))) 中的文件C:\Users\schappell\Downloads\test.py",第 5 行return __import__('functools').wraps(a)(lambda *c: b(a.__annotations__, 'return', a(*[b(a.__annotations__, *d) for d in zip(a.__code__.co_varnames), C)])))文件C:\Users\schappell\Downloads\test.py",第 3 行,在 b 中如果 b 在 a 而不是 isinstance(c, a[b]): raise TypeError('{} 应该是 {}, not {}'.format(b, a[b], type(c)))类型错误:class_or_type_or_tuple 应该是 (, ),而不是

问题:

  1. 为什么带有生成器的第一个函数有时会工作而有时会失败?
  2. 为什么一个生成器不被认为是一个序列(因为它生成了一个序列)?
  3. 当生成器在某些时候显然可以工作时,为什么需要一个序列?

解决方案

  1. 因为 isinstance 和其他几个棘手的标准库函数一样,当你给它一个元组时,它会做与其他序列不同的事情.也就是说,它可以工作,并检查类型是否是给定的任何类型.
  2. 因为它不是.请参阅序列协议定义.它需要将 __getitem__ 实现为一个.
  3. 一个错误,它仍然尚未合并,它告诉你你的生成器坏了,但错误消息不正确.

另外,请不要用这样的类型检查来玷污我们可爱的语言,除非有充分的理由:)

While trying to write a tiny, obfuscated type checker, an unacceptable code pattern was discovered. However, it inconsistently fails to work properly. This is the code that was initally written to test it with.

def statictypes(a):
    def b(a, b, c):
        if b in a and not isinstance(c, a[b]): raise TypeError('{} should be {}, not {}'.format(b, a[b], type(c)))
        return c
    return __import__('functools').wraps(a)(lambda *c: b(a.__annotations__, 'return', a(*(b(a.__annotations__, *d) for d in zip(a.__code__.co_varnames, c)))))

@statictypes
def isallinstance(iterable: object, class_or_type_or_tuple: (type, tuple)) -> bool:
    """isallinstance(iterable, class_or_type_or_tuple) -> bool

    Return whether all items in an iterable are instances of a class or of a
    subclass thereof. With a type as second argument, return whether that is
    all items' type. The form using a tuple, isallinstance(x, (A, B, ...)),
    is a shortcut for any(isallinstance(x, y) for y in (A, B, ...)).
    """
    return all(isinstance(item, class_or_type_or_tuple) for item in iterable)

The following shows a conversation with Python's interpreter and highlights the error that comes up. A TypeError is generated, but not the one that was expected. While generators were fine, now they fail.

>>> isallinstance(range(1000000), int)
True
>>> isallinstance(range(1000000), (int, float))
True
>>> isallinstance(range(1000000), [int, float])
Traceback (most recent call last):
  File "<pyshell#26>", line 1, in <module>
    isallinstance(range(1000000), [int, float])
  File "C:\Users\schappell\Downloads\test.py", line 5, in <lambda>
    return __import__('functools').wraps(a)(lambda *c: b(a.__annotations__, 'return', a(*(b(a.__annotations__, *d) for d in zip(a.__code__.co_varnames, c)))))
TypeError: isallinstance() argument after * must be a sequence, not generator

The statictypes function can be rewritten, and the isallinstance function redefined and wrapped. The simplest solution is to rewrite the generatior in statictypes to be a list comprehension.

def statictypes(a):
    def b(a, b, c):
        if b in a and not isinstance(c, a[b]): raise TypeError('{} should be {}, not {}'.format(b, a[b], type(c)))
        return c
    return __import__('functools').wraps(a)(lambda *c: b(a.__annotations__, 'return', a(*[b(a.__annotations__, *d) for d in zip(a.__code__.co_varnames, c)])))

After that, the isallinstance will starting working as expected once it is recreated from scratch. The TypeError stating what was wrong with the second argument is properly generated as was desired.

>>> isallinstance(range(1000000), int)
True
>>> isallinstance(range(1000000), (int, float))
True
>>> isallinstance(range(1000000), [int, float])
Traceback (most recent call last):
  File "<pyshell#29>", line 1, in <module>
    isallinstance(range(1000000), [int, float])
  File "C:\Users\schappell\Downloads\test.py", line 5, in <lambda>
    return __import__('functools').wraps(a)(lambda *c: b(a.__annotations__, 'return', a(*[b(a.__annotations__, *d) for d in zip(a.__code__.co_varnames, c)])))
  File "C:\Users\schappell\Downloads\test.py", line 5, in <listcomp>
    return __import__('functools').wraps(a)(lambda *c: b(a.__annotations__, 'return', a(*[b(a.__annotations__, *d) for d in zip(a.__code__.co_varnames, c)])))
  File "C:\Users\schappell\Downloads\test.py", line 3, in b
    if b in a and not isinstance(c, a[b]): raise TypeError('{} should be {}, not {}'.format(b, a[b], type(c)))
TypeError: class_or_type_or_tuple should be (<class 'type'>, <class 'tuple'>), not <class 'list'>

Questions:

  1. Why does the first function with the generator somtimes work and other times fail?
  2. Why is a generator not considered a sequence (since it generates a sequence)?
  3. Why is a sequence needed when a generator obviously works some of the time?

解决方案

  1. Because isinstance, like a couple of other screwy standard library functions, does a different thing when you give it a tuple than other sequences. Namely, it works, and checks that the type is any of the ones given.
  2. Because it isn't. See the sequence protocol definition. It'd need to implement __getitem__ to be one.
  3. A bug, which still hasn't been merged, which is telling you that your generator is broken, but with the incorrect error message.

Also, please don't dirty our lovely language with type checking like this for anything but good reasons :).

这篇关于类型错误:* 后的 function() 参数必须是序列,而不是生成器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆