debugging 出现TypeError,如何修复它?

blmhpbnm  于 2022-11-14  发布在  其他
关注(0)|答案(2)|浏览(153)

我经常会从Python代码中得到未捕获的异常(错误),这些异常被描述为TypeError s。经过大量的实验和研究,我能够收集到以下示例(和小的变化):

TypeError: func() takes 0 positional arguments but 1 was given
TypeError: func() takes from 1 to 2 positional arguments but 3 were given
TypeError: func() got an unexpected keyword argument 'arg'
TypeError: func() missing 1 required positional argument: 'arg'
TypeError: func() missing 1 required keyword-only argument: 'arg'
TypeError: func() got multiple values for argument 'arg'
TypeError: MyClass() takes no arguments
TypeError: unsupported operand type(s) for +: 'int' and 'str'
TypeError: can only concatenate str (not "int") to str
TypeError: '>' not supported between instances of 'int' and 'str'
TypeError: can't multiply sequence by non-int of type 'float'
TypeError: string indices must be integers
TypeError: %d format: a number is required, not str
TypeError: not all arguments converted during string formatting
TypeError: list indices must be integers or slices, not str
TypeError: int() argument must be a string, a bytes-like object or a number, not 'list'
TypeError: a bytes-like object is required, not 'str'
TypeError: bad operand type for abs(): 'str'
TypeError: descriptor 'to_bytes' for 'int' objects doesn't apply to a 'str' object
TypeError: 'int' object is not iterable
TypeError: cannot unpack non-iterable int object
TypeError: 'int' object is not callable
TypeError: 'int' object is not subscriptable

我还看到过在尝试使用库中的函数、方法或类时出现的自定义消息。
什么是TypeError?类似这样的消息意味着什么?我如何理解并解决问题?
如果您的问题是重复的,请仔细阅读并遵循这里的建议,并尝试debug代码和research任何遗留问题,然后再问。堆栈溢出不是调试服务。
关于TypeError的一个有效的、非重复的问题将询问为什么specific, minimal, reproducible example会导致TypeError,并解释你预期会发生什么以及为什么。

wmomyfyw

wmomyfyw1#

什么是TypeError

它的意思和它听起来的一样:存在由代码中的一个或多个值的Type引起的Error

......但什么是“类型”?

在Python程序中,每个对象都有一个 type。“object”(相当于Python中的“value”)是指可以在源代码中被赋予 name 的东西。大多数名称都是简单的变量:如果我们写x = 1,那么1是一个对象,它有一个名称x,它类型是int-类型本身有一个名称。
“类型”的意思或多或少就是它听起来的样子:它告诉你其他的东西是什么样的123都是整数它们有相同的类型,int。你可以把它看作是一个整数的概念。
并不是每个类型都有一个内置的名称。例如,函数是对象(大多数其他语言都不是这样工作的!),它们有一个类型,但我们不能在代码中直接通过名称引用该类型。
每一个类型都有一个对象的表示,不管它是否被命名,你可以通过use the built-in type得到这样一个“类型对象”:

>>> type(1) # the result from this...
<class 'int'>
>>> int # is the same:
<class 'int'>
>>> type(int) # We can look a bit deeper:
<class 'type'>
>>> def func():
...     pass
>>> type(func) # and get types that aren't named:
<class 'function'>
>>> type(type) # and there's this special case:
<class 'type'>

值得注意的是,type * 的类型是type本身 *。
你可能注意到Python 3.x将这些类型对象显示为单词class,这是一个有用的提示:当你创建一个class的时候,你是在 * 定义一个新的数据类型 *,这就是类的作用。

这类消息是什么意思?

我们可以将这些示例分为几类:

TypeError: func() takes 0 positional arguments but 1 was given
TypeError: func() takes from 1 to 2 positional arguments but 3 were given
TypeError: func() got an unexpected keyword argument 'arg'
TypeError: func() missing 1 required positional argument: 'arg'
TypeError: func() missing 1 required keyword-only argument: 'arg'
TypeError: func() got multiple values for argument 'arg'
TypeError: MyClass() takes no arguments

这些异常告诉你,调用func(或创建MyClass的示例)的参数(放在()之间的东西)是错误的,要么太多,要么不够,要么没有正确地标记。
这确实有点令人困惑。我们试图调用一个函数,而我们调用的是一个函数--所以类型实际上是匹配的。已确定的问题是 number of 参数。然而,Python将其报告为TypeError而不是ValueError。这可能是为了让其他语言(如C++)的程序员看起来更熟悉。其中“类型”在编译时被检查,并且可能非常复杂-例如,接受不同类型或参数数量的函数本身被认为具有不同的类型。

TypeError: unsupported operand type(s) for +: 'int' and 'str'
TypeError: can only concatenate str (not "int") to str
TypeError: '>' not supported between instances of 'int' and 'str'
TypeError: can't multiply sequence by non-int of type 'float'
TypeError: string indices must be integers

这些异常告诉您 operator 的左侧和右侧(+>^等符号,用于 * 计算结果 *)don't make sense。例如,尝试对字符串进行除法或减法运算,或repeat a string a non-integer number of times。作为特殊情况,可以在两个字符串之间使用+如果你试图在一个整数和一个字符串之间使用+,错误信息会根据顺序的不同而不同。

TypeError: %d format: a number is required, not str
TypeError: not all arguments converted during string formatting

%运算符用于得到 modulus(除法时的余数),但它也可以通过替换一些占位符来格式化字符串。(这是一个过时的系统,它是hard to get righthas weird special cases;在新代码中,请使用f字符串或.format方法。)
发生错误的原因是字符串左边的占位符与右边的不匹配。在第二种情况下,很可能您实际上要计算一个模数,因此左边应该是一个数字(很可能是整数)。是否应该将其改为ValueError s是有争议的,因为字符串的 contents 可能是错误的。然而,Python无法读取你的想法。

TypeError: list indices must be integers or slices, not str

这也是一个运算符的问题,这次是[]运算符(用于索引列表、切片列表或在字典中查找键)。如果我们在字典中查找键,而字典中的键是字符串,那么字符串在[]中是有意义的;但是我们不能用它索引到列表中。

TypeError: int() argument must be a string, a bytes-like object or a number, not 'list'
TypeError: a bytes-like object is required, not 'str'
TypeError: bad operand type for abs(): 'str'

这意味着something wrong被传递给了一个内置函数(或者另一个可调用函数,比如一个类型)。你从库中得到的函数可能会用自定义消息引发它们自己的TypeError。消息应该很简单。

TypeError: descriptor 'to_bytes' for 'int' objects doesn't apply to a 'str' object

这个问题很不寻常,大多数问这个问题的人都不会遇到它(except maybe with the datetime standard library module),因为试图把一个方法当作函数来使用,但却给了它错误的self类型:比如int.to_bytes('1'),代码是错误的,因为'1'是字符串,字符串不支持.to_bytes,Python不会把字符串转换成整数;它不能给予AttributeError,因为to_bytes是在类 * 中查找的,而不是在字符串上查找的。

TypeError: 'int' object is not iterable
TypeError: cannot unpack non-iterable int object
TypeError: 'int' object is not callable
TypeError: 'int' object is not subscriptable

这些就是它们听起来的意思。例如,重复检查以获得单独的值。这种情况发生在for循环、解析以及试图转换为列表等时。消息的“unpack”变体是由于试图在不可迭代对象上执行use unpacking syntax而导致的。

"callable" means "able to be called";“调用”某个东西是在它后面写()(可能在()之间有参数)。像1('test')这样的代码没有意义,因为1不是函数(或类型)。
"subscriptable" means "able to be subscripted";这里,“下标”意味着使用切片语法(x[1:2:3]),或者索引或查找键(x['test'])。我们只能使用 sequences(如list s或str ings)和 mappings(如dict s)来实现这一点。

gudnpqoy

gudnpqoy2#

如何了解并解决问题?

首先,查看回溯,找出 * 代码中 * 错误发生的位置。如果是在库中,则回溯到代码使用库的位置。然后仔细阅读错误消息,并将其与代码进行比较,找出 * 是什么 * 导致了错误。最后,仔细思考:是 * 操作 * 错误,还是 * 值 * 错误?
示例
(待办事项)
一些不明显的事情

重用名称

您是否尝试过reuse a name for two different things(例如,函数和它使用的一些全局数据)?
Python中的名称一次只能引用一个对象。如果你使用list作为变量名,那么它就不再是“列表的抽象概念”的名称,所以你不能用它来创建更多的列表如果创建一个包含字符串列表的全局变量months,然后写一个函数months,这个函数替换了这个列表,这个函数的代码不能查找这个列表,这种情况很容易发生。
类似地,如果你试图创建一个uses the same name for an method as for a data attribute of the instances类,也会导致同样的问题。(还有一个tricky special case with @staticmethod)。

处理列表

有时候,人们希望能够像Numpy数组一样使用列表,并向列表中的每个元素“广播”一个operationfunction call,但这并不奏效。

处理None

考虑你是否need to handle None as a special case.但首先要尽量避免陷入那种情况;正如他们所说的,“特殊情况还没有特殊到可以打破规则”。

尝试使用库(包括标准库)

如果有些东西没有像你期望的那样工作(例如,尝试subtract datetime.time sserialize an instance of a user-defined class as JSON)--而不是试图把这个问题当作调试问题来处理,搜索 * 你希望这部分代码做什么 * 的解决方案。

如果错误提到了“str”类型,而您认为它应该是一个数字

你是从input函数得到的吗?它会给你一个str,即使它看起来像一个数字。请看How can I read inputs as numbers?

如果错误提到'function'型别或'type'型别

您是否忘记了调用函数或创建类的示例?

有关错误参数的错误消息

错误消息将告诉您函数的名称;所以,看看调用函数的那一行,检查参数。位置参数的数量是否正确?是否有一个必须提供的关键字参数丢失了?是否有一个 * 不应该 * 提供的关键字参数?是否有一个位置参数也由keyword提供?
如果你正在为一个类写一个方法,remember to allow for self。它是示例方法的is necessary。如果你正在调用一个方法,请记住self将被算作一个参数(对于“required”数量和“given”数量)。
如果您使用的回呼会从间接来源取得参数,则check the source
如果您尝试建立自己类别的执行严修,并从__init__取得TypeErrormake sure that you actually wrote an __init__
如果你不知道参数应该是什么,检查文档。如果参数是有意义的,也许 * 函数 * 是错误的-确保你没有混淆它与同一个库中的另一个。

有关操作数类型的错误消息

确保运算符对于您希望代码执行的操作是正确的(例如:^is not exponentiation; you want **),然后检查操作数类型。
在大多数情况下,转换类型是合适的--但是要仔细考虑。确保操作对新类型是有意义的。例如,如果代码是l + 'second',而l是当前包含['first']list,我们很可能不想连接字符串。而是创建一个修改过的列表,它也包含'second'元素,所以实际上我们想要"add" another listl + ['second'] .
如果是string indices must be integers,则可能是被索引的字符串is JSON或类似的东西,它们应该已经被解析以创建字典(可能带有嵌套的列表和字典)。
如果是list indices must be integers or slices,很可能问题出在list上,而不是索引上。如果您预期listdict,请检查它是否 * 包含 * dict-特别是如果它只包含一个元素,这是一个dict。然后检查它是否是真正应该检查的dict。如果是,解决方案很简单:只需添加另一级索引,以便首先获取dict

关于字符串格式的错误消息

说真的,你 * 打算 * 做字符串格式化吗?如果你真的想格式化一个字符串,consider using f-strings or the .format method--这些更容易调试,并且有更少的特殊情况。但是更有可能的是,左手边是一些字符串,比如'1',它应该首先被转换成int(或者 * 可能是 * float)。

关于“描述符”的错误消息

Python的错误信息相当隐晦--它使用了大多数程序员很少需要担心的术语。但是一旦识别出错误,就很容易进行模式匹配。如果类可以在没有参数的情况下示例化,要特别注意--一对空括号()仍然是示例化类所必需的;否则,代码将引用 * 类本身 *。2为了使用方法,需要一个示例。

来自内置函数的自定义错误消息

“一元”运算符的“错误操作数”(例如bad operand type for unary +: 'str'can be caused by a stray comma'a', + 'b''a' + 'b'不同;它试图使用+作为'b'字符串的一元运算符,然后生成一个元组。(你知道如何写-1来得到一个负数吗?这里的-是一个 * 一元运算符 *。事实证明,你可以类似地写+1;它的意思当然与1相同。)
特别是如果您必须将代码从Python 2.x迁移到Python 3.x,那么在Python 3.x中要特别注意bytesstr类型之间的区别。bytes表示 * 原始数据 *; str表示 text。它们是根本不同且不相关的东西,并且只有通过使用 encoding 才可能从一个转换到另一个。在Python 3.x中,以二进制模式打开的文件(在模式字符串中使用'b')在读取时产生bytes,并且在写入时必须给出与bytes兼容的内容。str不合格;必须显式指定编码。此问题的标准编码是TypeError: a bytes-like object is required, not 'str' when writing to a file in Python 3

错误消息,其中某些内容以某种方式“不可用”

不可迭代

is not iterable出现问题时,问题很可能出在这个东西上,而不是迭代上。如果你想让一个for循环运行特定的次数,你仍然需要一些东西来迭代; a range是常见的选择。如果你对make multiple copies of a value使用列表解析等,也是如此。如果你有一个整数x,并且你想创建一个包含一个项目的 * 列表,也就是整数 *,拼写是[x],而不是list(x)
尤其常见的是'NoneType' object is not iterable'NoneType' object只有一个:特殊值None- Python禁止创建该类的任何示例。就地工作的Python方法-especially list methods-通常返回None而不是被修改的列表。另请参见TypeError: 'NoneType' object is not iterable in Python

不可调用

如果是'module' object is not callable,那最有可能是因为you want a function or class from the module, that has the same name as the module, rather than the module itself.这个链接的例子是针对socket的标准库;其它常见的情况包括X1 E27 F1 X和X1 E28 F1 X。
还要确保代码不会在调用函数时记住结果,而不是记住 * 函数本身 *。这是期待“回调”函数的API的常见问题。(如果你需要提前选择参数,但实际上并不调用函数,请参阅Python Argument Binders。)有时人们也会记住try to provide the name of a function as a string,而不是提供函数本身。
初学者有时希望能够在数学公式中进行“隐式乘法”,就像在数学课上一样。在Python程序中(像其他流行的语言一样),像a(b + c)这样的代码不会将整数a乘以b + c的结果;它试图调用a,就像调用一个函数一样。请参阅Why do I get "TypeError: 'int' object is not callable" from code like "5(side_length**2)"?

不可订阅

有时候,人们试图通过索引一个数字来获取它的“位数”,就好像它是一个字符串一样。intfloat的值为aren't strings; they don't have digits in them。因此,这将导致一个“不可下标”的TypeError。无论你用什么基数来写它们,数值都是相同的,而且除了基数10之外,还有其他的方法来写一个数字;因此首先创建适当的字符串是您的责任。
如果你正在尝试使用嵌套列表,在索引时要小心。像example = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]这样的列表应该像example[i][j]not e.g. example[i[j]]那样被索引。这里的逻辑应该非常简单:正确的代码意味着索引到example(得到一个整数列表),然后索引到那个结果。不正确的代码意味着首先使用j作为i的索引,因为括号是如何嵌套的。

相关问题