我遇到了一个 TypeError 错误,该怎么办?

11

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

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?这样的消息是什么意思?我如何理解并修复问题?


如果您的问题被关闭为重复,请仔细阅读并遵循此处的建议,在代码调试研究任何剩余的问题之前。Stack Overflow不是一个调试服务。

关于TypeError的有效且非重复的问题将会询问为什么一个特定、最小、可重现的示例会导致TypeError,并解释你期望的结果以及原因。


这个问题旨在作为人工规范的重复项,以关闭有关TypeError的简单问题。请不要尝试将其关闭为其他内容的副本;这里的目标是足够一般地描述TypeError以涵盖常见问题,同时仍然对所有问题提出相同的问题。请参见我遇到了IndentationError。我该如何解决?以获取先例。此答案也至少部分存在于帮助找到特定TypeError原因的更多特定规范方面。 - Karl Knechtel
我认为“NoneType”错误很常见,应该有自己独特的规范处理方式。 - Mark Ransom
@MarkRansom 我的意图是让这篇文章关于阅读和理解错误信息的过程。NoneType是一种类型,而不是一个错误;可能会有多个TypeError提到它(更不用说AttributeError:'NoneType'对象没有属性'foo'了,这比那些常见得多)。然而,你当然可以自由地起草自己的规范。 - Karl Knechtel
2个回答

12

什么是TypeError

它的意思就是字面上的意思:代码中一个或多个值的Type引起了一个Error

... 但是"Type"是什么意思?

在Python程序中,每个对象都有一个type。通过"object"(在Python中等价于"value")我们指的是可以在源代码中被赋予name的东西。大多数名称都是简单变量:如果我们写x = 1,那么1是一个对象,它有一个名字x,它的类型是int - 类型本身也有一个名字。

"Type"的意思更多或少是它听起来的意思:它告诉你关于某个东西的种类123都是整数;它们有相同的类型,int。您可以将其视为表示整数的概念

并非每种类型都有内置名称。例如,函数是对象(大多数其他语言不会这样工作!),它们有一种类型,但我们无法直接在代码中引用该类型的名称。

然而,每种类型都有一个表示为对象的表示,无论它是否具有名称。您可以使用内置的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实例)时括号内的参数(放在()之间的东西)是错误的。要么太多,不够,或者标签不正确。
这可能有点令人困惑。我们试图调用一个函数,而我们正在调用的东西是一个函数 - 所以类型确实匹配。确定的问题在于参数的数量。但是,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

这些异常是在告诉你左侧和右侧的运算符(像“+”、“>”或“^”这样的符号,用于计算结果)没有意义。例如,尝试除去或减去字符串,或者重复一个非整数次数的字符串,或者(在3.x中)将字符串与数字进行比较。作为特例,可以在两个字符串(或列表、元组)之间使用+,但它不会以数学意义上的方式“添加”它们。如果你尝试在整数和字符串之间使用+,错误消息将根据顺序而异。
TypeError: %d format: a number is required, not str
TypeError: not all arguments converted during string formatting

这些有点棘手。操作符%用于获取模数(在除法时剩下的余数),但它也可以用于通过替换一些占位符来格式化字符串。(这是一个过时的系统,很难搞对并且有奇怪的特例; 在新代码中,请使用f-strings或.format方法。)
发生错误是因为左侧字符串中的占位符与右侧不匹配。在第二种情况下,您可能实际上想要计算模数,因此左侧应该是数字(最可能是整数)。是否应将其视为ValueError存在争议,因为字符串的内容可能是错误的。但是,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'

这意味着在内置函数(或其他可调用对象,如类型)中传递了某些错误。从库中获取的函数可能会引发其自己的TypeError,并带有自定义消息。该消息应该非常直观。
TypeError: descriptor 'to_bytes' for 'int' objects doesn't apply to a 'str' object

这是一个非常不寻常的问题,大多数问这个问题的人都不会遇到它(除了可能与datetime标准库模块一起使用)。这是因为尝试将方法用作函数,但为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

这些词汇的含义就是它们听起来的意思。"iterable" 意味着"可迭代",即可以重复检查以获得单独的值。这发生在 for 循环、推导式和尝试转换为列表等情况中。消息的 "unpack" 变体是由于尝试在非可迭代对象上使用解包语法而产生的。
"callable" 意味着 "可调用";"调用" 某个东西是在它后面写 ()(可能在括号内带有参数)。像 1('test') 这样的代码是没有意义的,因为 1 不是一个函数(或类型)。

"subscriptable" means "able to be subscripted";在这里,“subscripting”指的是使用切片语法(x[1:2:3]),或者索引或查找键(x['test'])。我们只能对序列(如liststr)和映射(如dict)进行此操作。


6

如何理解并解决问题?

首先,查看回溯以确定错误发生的代码位置。如果是在库中,反推到您的代码使用该库的地方。然后仔细阅读错误消息,并将其与代码进行比较,以确定引起投诉的原因。最后,请认真思考:操作有误还是值有误?

示例

(待定)

一些不明显的事情

重复使用名称

您是否重新分配内置可调用函数的名称,例如 strinputlist?您是否尝试为两个不同的事物重复使用名称(例如函数和一些它使用的全局数据)?

Python中的名称一次只能引用一个东西。如果您使用例如list作为变量名,那么它就不再是“列表的抽象概念”的名称,因此您不能使用它来创建更多的列表(包括将其他内容转换为列表)。如果您创建了一个具有字符串列表的全局变量months,然后编写一个名为months的函数,则该函数将替换列表,并且函数的代码无法查找列表。这很容易在意外情况下发生当使用from some_module import *语法时
同样,如果您尝试创建一个类使用与实例的数据属性相同的方法名称,那么也会导致同样的问题。(还有一个带有@staticmethod的棘手特殊情况)。

处理列表

有时人们期望能够像使用Numpy数组一样,将操作函数调用“广播”到列表的每个元素上。但是这是行不通的。使用列表推导式代替

处理None

考虑是否需要将None作为特殊情况处理。但是尽量避免首先进入这种情况;正如所说的,“特殊情况不足以打破规则”。

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

如果某些东西不像您期望的那样工作(例如,尝试从时间中减去datetime.time将用户定义类的实例序列化为JSON)-而不是试图将问题视为调试问题,请搜索解决方案以满足您希望该部分代码完成的内容

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

你是不是从input函数中获取的?这会给你一个str类型,即使它看起来像一个数字。请参见如何将输入读取为数字?

如果错误提到了“function”类型或“type”类型

你是否忘记调用函数或创建类的实例?


关于错误参数的错误信息

错误信息会告诉你函数的名称;因此,请查看调用该函数的行的部分,并检查参数。是否有正确数量的位置参数?是否需要提供关键字参数,但缺少了?是否提供了不应该提供的关键字参数?是否提供了由关键字和位置两种方式提供的参数?

如果您正在为类编写方法,请记得允许使用 self。这对于实例方法是必要的。如果您正在调用方法,请记住self将被计算为一个参数(既适用于“所需”的数量,也适用于“给定”的数量)。

一个常见的初学者错误是尝试在没有实例化的情况下使用类中的方法(它不是一个classmethod)。您可能会做一些像这样的事情:

value = MyClass.method(things)

你应该做类似这样的事情

instance = MyCLass()
value = instance.method(things)

这段代码(含糊不清地)将instance作为第一个(self)参数传递给method,将things作为第二个参数。

如果您正在使用从间接来源获取参数的回调,请检查来源

如果您尝试创建自己类的实例,并从__init__获得TypeError,请确保您实际编写了__init__

如果您不知道参数应该是什么,请检查文档。如果参数有意义,也许函数是错误的 - 确保您没有将其与同一库中的另一个函数混淆。

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

确保运算符对于您想要代码执行的操作正确(例如:^不是指数运算;您需要**),然后检查操作数类型。

在大多数情况下,转换类型是合适的 - 但要仔细考虑。确保操作与新类型一起有意义。例如,如果代码是l + 'second',而l是当前包含['first']list,那么很可能我们不想连接字符串,而是创建一个修改后的列表,其中也有'second'作为元素。因此,实际上我们想要"添加" another list: l + ['second']
如果string indices must be integers,那么被索引的字符串可能是JSON或类似的东西,应该已经解析为字典(可能带有嵌套的列表和字典)。
如果出现“列表索引必须是整数或切片”错误,很可能问题出在列表本身,而不是索引上。如果你期望的列表是一个字典,请检查它是否包含一个字典——特别是如果它只包含一个元素,而这个元素是一个字典。然后检查是否应该查找的确实是这个字典。如果是这样,解决方案很简单:只需添加另一层索引,以便首先获取那个字典。这通常发生在尝试从解析的JSON中获取数据时。
关于字符串格式的错误消息

说真的,你是想做字符串格式化吗?如果确实想要格式化一个字符串,考虑使用f-strings或者.format()方法。这些方法更容易调试,也没有太多特殊情况。但更可能的情况是,左边的部分是一些字符串,比如'1',本应该先转换为int(或者可能float)。

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

Python的这个错误消息相当难以理解——它使用了大多数程序员很少或者从未遇到的术语。但是,一旦被认出来,这个错误就变得非常容易匹配。特别注意,如果类可以不带参数实例化,则需要一个空括号 ()才能实例化该类;否则,代码将引用类本身。必须有一个实例才能使用方法。

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

一个“一元”运算符的“错误操作数”(例如bad operand type for unary +: 'str'可能是由于一个杂项逗号引起的'a', + 'b'不同于'a' + 'b';它试图在'b'字符串上使用+作为一元运算符,然后生成一个元组。(你知道你可以写例如-1来得到一个负数吗?那里的-是一个一元运算符。当然,你也可以类似地写+1;它的意思和1是一样的。)

特别是如果你不得不从2.x迁移代码到3.x,一定要非常小心区分3.x中bytesstr类型之间的区别。 bytes表示原始数据; str表示文本。这些是根本不同且无关的事情,只有通过使用编码才能从一个转换为另一个。在Python 3.x中,以二进制模式打开的文件(在模式字符串中使用'b')读取时会产生bytes,写入时必须给出与bytes兼容的内容。 str不符合条件;您必须明确指定编码。处理Python 3中的文件内容时,此问题的规范是{{link1:“TypeError:a bytes-like object is required, not 'str'”}}。

某些东西“不可用”的错误消息

您想以这种方式使用它吗?

Python无法读取您的意图。例如,访问列表元素使用[]而不是()。如果代码使用(),那么它将被解释为尝试调用列表,因此错误消息将抱怨列表不可调用。

不可迭代

当某物不可迭代时,问题很可能出在该物体上,而不是迭代上。如果您想要一个for循环运行特定次数,仍然需要有东西进行迭代;range通常是选择。如果您正在使用列表推导等来制作多个值的副本。如果您有一个整数x,并且您想要制作一个只包含该整数的列表项的列表,那么应该写成[x],而不是list(x)

在编程中,经常会看到'NoneType' object is not iterable。实际上只有一个'NoneType' object':特殊值None - Python禁止创建该类的任何其他实例。Python中的原地操作方法 - 尤其是列表方法 - 通常返回修改后的列表而不是None。另请参见Python中的TypeError:'NoneType' object is not iterable

不可调用

如果'module' object is not callable,最可能的原因是您想要从模块中获取与模块同名的函数或类,而不是模块本身。链接的示例是标准库socket;其他常见情况包括datetimerandom

同时确保代码不要调用函数并记住结果,而是记住函数本身。这是API常见的问题,它希望有一个“回调”函数。(如果您需要提前选择参数,但实际上不需要调用函数,请参见如何在Python中绑定函数参数?。)有时人们也会尝试提供函数名作为字符串, 而不是提供函数本身。

初学者有时期望在数学公式中能够进行“隐式乘法”,就像在数学课堂上一样。 在Python程序(像其他流行的语言一样),代码如a(b + c)不会将整数a乘以b + c的结果;它试图调用a,就像它是一个函数一样。 请参见为什么我会从像"5(side_length**2)"这样的代码中得到“TypeError:'int' object is not callable”错误消息?

不可订阅

有时候,人们试图通过像字符串一样索引数字来获取“数字”。intfloat不是字符串;它们里面没有数字。因此,这将导致“is not subscriptable”TypeError。无论您用什么基数编写数字,其数值都是相同的,并且除了十进制之外,还有其他编写数字的方法;因此,您需要先创建适当的字符串。
如果您正在尝试使用嵌套列表,请小心索引它们。像example = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]这样的列表应该像example[i][j]这样索引,而不是像 example[i[j]]这样。这里的逻辑应该很简单:正确的代码意味着要索引到example(获取整数列表),然后再索引到该结果。不正确的代码意味着首先使用j作为i的索引,因为括号是嵌套的。

如果你想调用一个函数或使用一个类(例如内置的range),请记住使用圆括号而不是方括号

# WRONG
range[10]
# RIGHT
range(10)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接