Python中的“断言”有什么用?

assert是什么意思?它是如何使用的?

1225302 次浏览

assert语句几乎存在于每种编程语言中。它有两个主要用途:

  1. 它有助于在程序的早期发现问题,原因很清楚,而不是稍后当其他操作失败时。例如,Python中的类型错误,如果没有及早发现,可能会在实际引发Exception之前经过几层代码。

  2. 它为阅读代码的其他开发人员留档,他们看到assert并可以自信地说它的条件从现在开始成立。

当你这样做…

assert condition

…您告诉程序测试该条件,如果条件为假,则立即触发错误。

在Python中,它大致相当于:

if not condition:raise AssertionError()

在Python shell中尝试一下:

>>> assert True # nothing happens>>> assert FalseTraceback (most recent call last):File "<stdin>", line 1, in <module>AssertionError

断言可以包含可选消息,您可以在运行解释器时禁用它们。

如果断言失败,则打印消息:

assert False, "Oh no! This assertion failed!"

没有使用括号像函数一样调用assert。它是一个语句。如果您执行assert(condition, message),您将运行assert,并将(condition, message)元组作为第一个参数。

至于禁用它们,当在优化模式下运行python时,其中__debug__False,断言语句将被忽略。只需传递-O标志:

python -O script.py

相关留档见这里

从文档:

断言语句是将调试断言插入程序的便捷方法

你可以在这里阅读更多:http://docs.python.org/release/2.5.2/ref/assert.html

其他人已经给你留档链接。

您可以在交互式shell中尝试以下操作:

>>> assert 5 > 2>>> assert 2 > 5Traceback (most recent call last):File "<string>", line 1, in <fragment>builtins.AssertionError:

第一条语句什么都不做,而第二条语句引发异常。这是第一个提示:断言有助于检查代码给定位置(通常是函数的开头(前置条件)和结尾(后置条件))中应该为真的条件。

断言实际上与契约编程高度相关,这是一个非常有用的工程实践:

http://en.wikipedia.org/wiki/Design_by_contract

正如其他答案所指出的,assert类似于在给定条件不为真时抛出异常。一个重要的区别是,如果您使用优化选项#1编译代码,断言语句会被忽略。留档assert expression可以更好地描述为等效于

if __debug__:if not expression: raise AssertionError

如果你想彻底测试你的代码,然后在你很高兴没有断言用例失败时发布一个优化版本,这会很有用——当优化打开时,__debug__变量变成False,条件将停止评估。如果你依赖断言而没有意识到它们已经消失,这个功能也会发现你。

断言语句有两种形式。

简单形式assert <expression>等价于

if __​debug__:if not <expression>: raise AssertionError

扩展形式assert <expression1>, <expression2>等价于

if __​debug__:if not <expression1>: raise AssertionError(<expression2>)

这是一个简单的例子,将其保存在文件中(假设b.py)

def chkassert(num):assert type(num) == int

chkassert('a')

$python b.py时的结果

Traceback (most recent call last):File "b.py", line 5, in <module>chkassert('a')File "b.py", line 2, in chkassertassert type(num) == intAssertionError

断言是一种系统的方法,用于检查程序的内部状态是否符合程序员的预期,目的是捕获错误。参见下面的示例。

>>> number = input('Enter a positive number:')Enter a positive number:-1>>> assert (number > 0), 'Only positive numbers are allowed!'Traceback (most recent call last):File "<stdin>", line 1, in <module>AssertionError: Only positive numbers are allowed!>>>

如果assert之后的语句为真,则程序继续,但如果assert之后的语句为假,则程序会给出错误。就这么简单。

例如:

assert 1>0   #normal executionassert 0>1   #Traceback (most recent call last):#File "<pyshell#11>", line 1, in <module>#assert 0>1#AssertionError

格式:断言表达式[,参数]当assert遇到语句时,Python会评估表达式。如果语句不为真,则引发异常(assertionError)。如果断言失败,Python将使用ArgumentExpress作为AssertionError的参数。AssertionError异常可以像任何其他异常一样使用try-除外语句捕获和处理,但如果不处理,它们将终止程序并产生回溯。示例:

def KelvinToFahrenheit(Temperature):assert (Temperature >= 0),"Colder than absolute zero!"return ((Temperature-273)*1.8)+32print KelvinToFahrenheit(273)print int(KelvinToFahrenheit(505.78))print KelvinToFahrenheit(-5)

执行上述代码时,会产生以下结果:

32.0451Traceback (most recent call last):File "test.py", line 9, in <module>print KelvinToFahrenheit(-5)File "test.py", line 4, in KelvinToFahrenheitassert (Temperature >= 0),"Colder than absolute zero!"AssertionError: Colder than absolute zero!
def getUser(self, id, Email):
user_key = id and id or Email
assert user_key

可用于确保参数在函数调用中传递。

注意括号。正如在其他答案中指出的,在Python 3中,#0仍然是一个语句,所以通过类比print(..),人们可以推断出assert(..)raise(..),但你不应该。

这是错误的:

assert(2 + 2 == 5, "Houston we've got a problem")

这是正确的:

assert 2 + 2 == 5, "Houston we've got a problem"

第一个不起作用的原因是bool( (False, "Houston we've got a problem") )的计算结果为True

在语句assert(False)中,这些只是False周围的冗余括号,它们的计算结果是它们的内容。但是对于assert(False,),括号现在是一个元组,非空元组在布尔上下文中的计算结果是True

如果你想确切地知道保留函数在python中的作用,请输入help(enter_keyword)

确保如果要输入保留关键字,请将其作为字符串输入。

Python中断言的目标是告知开发人员程序中的无法恢复错误。

断言不是为了表示预期的错误条件,例如“未找到文件”,用户可以在其中采取纠正措施(或重试)。

另一种方式是说断言在你的代码中是内部自查。它们通过在你的代码中声明一些条件为不可能来工作。如果这些条件不成立,那意味着程序中有一个bug。

如果你的程序没有bug,这些条件永远不会发生。但是如果其中一个确实发生,程序将崩溃,并显示一个断言错误,告诉你触发了哪个“不可能”的条件。这使得跟踪和修复程序中的错误变得更加容易。

这是我写的关于Python断言的教程的总结:

Python的assert语句是一种调试辅助工具,而不是处理运行时错误的机制。使用断言的目的是让开发人员更快地找到bug的可能根本原因。除非程序中有bug,否则不应引发断言错误。

>>>this_is_very_complex_function_result = 9>>>c = this_is_very_complex_function_result>>>test_us = (c < 4)
>>> #first we try without assert>>>if test_us == True:print("YES! I am right!")else:print("I am Wrong, but the program still RUNS!")
I am Wrong, but the program still RUNS!

>>> #now we try with assert>>> assert test_usTraceback (most recent call last):File "<pyshell#52>", line 1, in <module>assert test_usAssertionError>>>

如果assert关键字后面的代码是False,Python中的assert关键字会引发AssertionError。如果不是,它会继续,因为什么都没发生。

例1

a = 5b = 6
assert a == b

输出:

AssertionError

这是因为,显然,a不等于b。如果您想在代码中引发Exception,这特别有用。

def get_dict_key(d, k):try:assert k in dreturn d[k]except Exception:print("Key must be in dict.")

上面的例子实际上是无用的,但请记住,它主要用于调试目的,因此您可以跟踪您的错误。

Python断言基本上是一个调试辅助工具,用于测试代码的内部自检条件。当你的代码陷入不可能的边缘情况时,断言让调试变得非常容易。断言检查那些不可能的情况。

假设有一个函数可以计算折扣后的商品价格:

def calculate_discount(price, discount):discounted_price = price - [discount*price]assert 0 <= discounted_price <= pricereturn discounted_price

在这里,discounted_price永远不能小于0且大于实际价格。因此,如果违反上述条件,断言会引发断言错误,这有助于开发人员识别不可能发生的事情。

希望有帮助:)

我的简短解释是:

  • 如果表达式为false,则assert引发AssertionError,否则只是继续代码,如果有逗号,无论它是什么,它都将是AssertionError: whatever after comma,代码就像:raise AssertionError(whatever after comma)

关于这个的相关教程:

https://www.tutorialspoint.com/python/assertions_in_python.htm

正如在C2知识库上简要总结的那样:

断言是程序中特定点的布尔表达式,它将是true除非程序中有bug。

您可以使用assert语句来记录您对特定程序点代码的理解。例如,您可以记录有关输入(前置条件)、程序状态(不变量)或输出(后置条件)的假设或保证。

如果您的断言失败,这是对您(或您的继任者)的警告,即您在编写程序时对程序的理解是错误的,并且它可能包含bug。

有关更多信息,John Regehr有一篇关于断言的使用的精彩博客文章,它也适用于Pythonassert语句。

assert语句几乎存在于每种编程语言中。它有助于在程序的早期发现问题,原因很清楚,而不是稍后作为其他操作的副作用。他们总是期待True条件。

当你做这样的事情:

assert condition

您告诉程序测试该条件,如果它为假,则立即触发错误。

在Python中,#0表达式相当于:

if __debug__:if not <expression>: raise AssertionError

您可以使用扩展表达式传递可选消息

if __debug__:if not (expression_1): raise AssertionError(expression_2)

在Python解释器中尝试:

>>> assert True # Nothing happens because the condition returns a True value.>>> assert False # A traceback is triggered because this evaluation did not yield an expected value.Traceback (most recent call last):File "<stdin>", line 1, in <module>AssertionError

在使用它们之前有一些注意事项,主要是针对那些认为在assertif语句之间切换的人。使用assert的目的是在程序验证条件并返回一个应该立即停止程序的值时,而不是采取其他方法绕过错误:

1.括号

您可能已经注意到,assert语句使用了两个条件。因此,没有使用括号将它们作为一个明显的建议。如果你这样做:

assert (condition, message)

示例:

>>> assert (1==2, 1==1)<stdin>:1: SyntaxWarning: assertion is always true, perhaps remove parentheses?

您将使用表示元组的(condition, message)作为第一个参数运行assert,发生这种情况是因为Python中的非空元组是总是#2。但是,您可以单独执行,没有问题:

assert (condition), "message"

示例:

>>> assert (1==2), ("This condition returns a %s value.") % "False"Traceback (most recent call last):File "<stdin>", line 1, in <module>AssertionError: This condition returns a False value.

调试目的

如果你想知道什么时候使用assert语句。举一个现实生活中使用的例子:

*当您的程序倾向于控制用户输入的每个参数或其他任何参数时:

def loremipsum(**kwargs):kwargs.pop('bar') # return 0 if "bar" isn't in parameterkwargs.setdefault('foo', type(self)) # returns `type(self)` value by defaultassert (len(kwargs) == 0), "unrecognized parameter passed in %s" % ', '.join(kwargs.keys())

*另一种情况是在数学上,当0或非正作为某个方程的系数或常数时:

def discount(item, percent):price = int(item['price'] * (1.0 - percent))print(price)assert (0 <= price <= item['price']),\"Discounted prices cannot be lower than 0 "\"and they cannot be higher than the original price."
return price

*甚至是布尔实现的简单示例:

def true(a, b):assert (a == b), "False"return 1
def false(a, b):assert (a != b), "True"return 0

3.数据处理或数据验证

最重要的是不要依赖assert语句来执行数据处理或数据验证,因为该语句可以在Python初始化时使用-O-OO标志(分别表示值1、2和0(默认值))或PYTHONOPTIMIZE环境变量来关闭。

值1:

*断言被禁用;

*字节码文件使用#0扩展名而不是.pyc生成;

*sys.flags.optimize设置为1(True);

*并且,__debug__设置为False

值2:禁用更多内容

*docstring被禁用;

因此,使用assert语句来验证某种预期数据是极其危险的,甚至意味着一些安全问题。然后,如果您需要验证某些权限,我建议您使用raise AuthError。作为一个有效的先决条件,程序员通常在没有用户直接交互的库或模块上使用assert

与其他答案一样,assert语句用于检查状态在给定点上的程序。

我不会重复所说的有关消息、括号或-O选项和__debug__常量。请同时检查合同编程作为B. Meyer在[面向对象的软件构建](https://www.eiffel.org/doc/eiffel/Object-Oriented_Software_Construction%2C_2nd_Edition)并用[Eiffel编程语言]实现(https://en.wikipedia.org/wiki/Eiffel_(programming_language))。你不能完全使用assert语句通过合约模拟编程,但它是#36825;意保持原意

这是一个例子。假设您必须编写一个head函数(例如Haskell中的head函数http://www.zvon.org/other/haskell/Outputprelude/head_f.html))。the给定的规范是:“如果列表不为空,则返回列表的第一项”。查看以下实现:

>>> def head1(xs): return xs[0]

>>> def head2(xs):...     if len(xs) > 0:...         return xs[0]...     else:...         return None

(是的,这可以写成#0,但这不是重点).

如果列表不为空,则两个函数都有相同的结果和此结果正确答案:

>>> head1([1, 2, 3]) == head2([1, 2, 3]) == 1True

因此,两种实现(我希望)都是正确的。当你尝试获取空列表的头项:

>>> head1([])Traceback (most recent call last):...IndexError: list index out of range

但是:

>>> head2([]) is NoneTrue

同样,这两种实现都是正确的,因为没有人应该传递一个空的列出这些函数(我们是超出规范)。这是一个不正确的呼叫,但如果你做这样的呼叫,任何事情都可能发生。一个函数引发异常,另一个函数返回特殊值。最重要的是:我们不能依赖这种行为。如果xs为空,这将工作:

print(head2(xs))

但这会使程序崩溃:

print(head1(xs))

为了避免一些意外,我想知道当我通过一些意想不到的函数的参数。换句话说:我想知道何时可观察到行为是不可靠的,因为它取决于实现,而不是规范。当然,我可以阅读规范,但程序员并不总是仔细阅读文档。

想象一下,如果我有一种方法将规范插入代码以获取以下效果:当我违反规范时,例如通过传递一个空列表head,我得到一个警告。这将是一个很大的帮助,写一个正确的(即符合规范)程序。这就是assert进入现场:

>>> def head1(xs):...     assert len(xs) > 0, "The list must not be empty"...     return xs[0]

>>> def head2(xs):...     assert len(xs) > 0, "The list must not be empty"...     if len(xs) > 0:...         return xs[0]...     else:...         return None

现在,我们有:

>>> head1([])Traceback (most recent call last):...AssertionError: The list must not be empty

还有:

>>> head2([])Traceback (most recent call last):...AssertionError: The list must not be empty

注意head1抛出的是AssertionError,而不是IndexError重要的是因为AssertionError不是任何运行时错误:它发出信号违反规范。我想要一个警告,但我得到一个错误。幸运的是,我可以禁用检查(使用-O选项),但在我自己的风险。我将这样做一个崩溃是非常昂贵的,并希望最好。想象一下我的程序嵌入在一艘宇宙飞船中,它穿过一个黑洞。我会禁用断言,并希望程序足够强大尽可能长时间不崩溃。

这个例子只是关于先决条件,你可以使用assert来检查后置条件(返回值和/或状态)和不变量(状态类)。请注意,使用assert检查后置条件和不变量可以麻烦:

  • 对于后置条件,您需要将返回值分配给变量,并且如果你正在处理一个方法,可以存储对象的基本状态;
  • 对于不变量,您必须检查方法调用前后的状态。

你不会有像埃菲尔那样复杂的东西,但你可以#36825;提高节目的整体质量。


总而言之,assert语句是检测this的便捷方法不应该发生的情况。违反规范(例如通过空列表head)是第一类这不应该发生情况。因此,虽然assert语句可用于检测任何意外情况,这是确保规范得到满足的一种特权方式。一旦您将assert语句插入代码以表示我们希望你们已经提高了程序的质量,因为不正确的参数,不正确的返回值,类的不正确状态…,将被报道

如果你使用assertisinstance来声明对象的类型,它会让你在编码时访问父对象的方法和属性,它会自动完成。

例如,假设self.object1.object2MyClass对象。

import MyClasss
def code_it(self):testObject = self.object1.object2 # at this point, program doesn't know that testObject  is a MyClass object yetassert isinstance(testObject , MyClasss) # now the program knows testObject is a MyClass objecttestObject.do_it() # from this point on, PyCharm will be able to auto-complete when you are working on testObject

断言是在我们的程序中自信地陈述事实的语句。

语法:assert <condition>assert <condition>,<error message>

它有一个应该始终为真的条件/表达式。如果条件为假,assert语句将停止程序并抛出一条错误消息AssertionError。因此,您的断言表达式将是您在程序中不想要的东西。

e. g.

  1. assert <condition>--使用断言而不使用<error message>

    var = int(input("Enter value 1-9 inclusive:"))assert var!=0print(var)

    输出:

    如果输入为0:

    AssertionError

    如果输入为1:

    1
  2. assert <condition>,<error message>--使用<error message>的断言

    var = int(input("Enter value 1-9 inclusive:"))assert var!=0,"Input cannot be zero"print(var)

    输出:

    如果输入为0:

    AssertionError: Input cannot be zero

    如果输入为1:

    1

核心进展:

  1. 它被用作调试工具。
  2. 它接受一个表达式和一个可选消息。
  3. 它存在于几乎所有的编程语言中

断言是什么意思?它是如何使用的?

这里有很好的答案,但不是问题的第二部分。尽管有多年的实践经验,我最近才明白断言的目的。

其他人已经解释了如何使用assert,所以我非常简短。这就是你如何使用它:

assert condition, "error message"

并且不要使用括号,assert是关键字,不是函数。

assert (condition, "error message")  # wrong: this never fails!

这里给出了一个关于如何使用断言的很好的解释:http://wiki.c2.com/?WhatAreAssertionsassert的要点是在代码中声明不变量。不变量是一种永远不应该违反的条件,除非代码中有bug。将它们视为可执行的留档。这与面向对象编程如何封装来自外部世界的代码有着深刻的联系。

简而言之:对于其他开发人员,你应该在代码中使用断言,就像注释一样。但它比注释好,“注释”实际上是在调试模式下检查的!如果删除断言,你的程序无法正常工作,那你就错了。事实上,如果你打开优化,Python会忽略所有assertpython -O)。所以不要依赖它们在那里。特别是不要使用断言来验证用户输入。相反,引发异常。

这里有一个很好的例子可以将它们联系在一起。假设你想在代码中有一个特殊的数字类,它代表称为PositiveInt的正整数。你为什么要这样做?你有很多使用正整数作为参数的函数。通过在代码中使用PositiveInt,你不必在每个函数中一遍又一遍地检查输入是否有效。PositiveInt保证了这一点。粗略的实现如下所示

class PositiveInt(int):# int is immutable, so we have to override new and not initdef __new__(cls, value):if value <= 0:raise ValueError(f"{value} is not positive")assert value > 0, "value must be positive"return super(PositiveInt, cls).__new__(cls, value)

如您所见,我在函数末尾使用if ...: raise ...进行输入验证assert。这似乎是多余的,但仅限于这个微不足道的示例!想象一下,该函数有点长且更复杂,因此您是否正确验证了输入并不明显。末尾的断言确保检测到验证代码中的bug。它还使验证代码的意图向另一个程序员明确,而不仅仅是简单的注释。

总结:像注释一样使用assert。在任何地方都使用它!它很便宜,如果它成为您的用户的性能问题,您可以在发布python -O时将其关闭。