2022-07-22

Assert的用法
那么我们可以写一个 apply_discount 的 function ,并在结果返还之前,加一个 assert 语句。

def apply_discount(price, discount):
result = price * (1.0 - discount)
print(result)
assert 0 <= result <= price, “resulting price is unreasonable”
return resultCopyCOPY
我们可以用一个不合理的价格来测试一下,比如价格是99块,折扣是110%,99 x (1-1.1) = -9.9,也就是倒贴给买家9块9,我们知道这是不合理的。

price = 99
discount = 1.1

apply_discount(price, discount)CopyCOPY
接着运行:

$ python assert.pyCopyCOPY
Assert在这时就会弹出以下的error。

-9.90000000000001
Traceback (most recent call last):
File “d:\Python\assert.py”, line 10, in
apply_discount(price, discount)
File “d:\Python\assert.py”, line 7, in apply_discount
assert 0 <= result <= price, “resulting price is unreasonable”
AssertionError: resulting price is unreasonableCopyCOPY
(注意这里有 Rounding Error,因为计算机采用的是二进制浮点数,但这个问题不在本文的讨论范围内,具体原因可以请各位再搜索如何处理 Rounding Error)

assert 0 <= result <= price, “resulting price is unreasonable"CopyCOPY
这一句 Assert 的意思就是,我们要保证 result 在0到原价之间,如果不是,要抛出后面定义的 error 语句,也就是我这里写的"resulting price is unreasonable”。

此时我们再回过头来看Assert的语法:

assert_stmt ::= “assert” expression1 [“,” expression2]CopyCOPY
在这里,expression1 就是我们测试的 condition。而 expression2 是optional的,也就是验证为False时,我们想要其展示的error message。

Assert是如何实现的
在程序运行的时候,Python Interpreter 会将每一句 Assert statement 转换成以下的形式:

if debug:
if not expression1:
raise AssertionError(expression2)CopyCOPY
debug 在这里是一个 global variable,如果我们在运行的时候加上,“-O"或者”-OO"的 command ,会让 debug 的值变为 False,相当于运行时直接忽略Assert。

比如我们再运行一次:

$ python -O assert.pyCopyCOPY
输出就变成了:

-9.90000000000001CopyCOPY
会发现这里没有了 Assert Error 的部分。

用 Assert 时的两个注意点

  1. 不要用 Assert 来做 Data Validation
    比如书中提到的这段代码:

ddef delete_product(prod_id, user):
assert user.is_admin(), “Must be admin”
assert store.has_product(prod_id), “Unknown product”
store.get_product(prod_id).delete()CopyCOPY
首先这段代码想要实现的功能是删除商店中的一个商品,所以需要检查进行操作的是否为 Admin,以及商店是否的确有这个商品。

这里用 Assert 来检查用户是否为 Admin 是非常危险的,因为正如我们前面提到,在运行 command 加上"-O"可以直接在 Python Interpreter 层面 disable 掉 Assert。那么就会导致这个检查被跳过,也就是说在这里任意用户都可以进行删除商品的行为。

另一个问题在于这里的 has_product 如果被跳过了,那么最后一行调用 get_product() 的时候,就可以使用 invalid 的 product ID,很容易导致更严重的 bug。

解决办法就是绝对不要用 Assert 做 Data Validation,将这里的 Assert 改为 if-else:

def delete_product(prod_id, user):
if not user.is_admin():
raise AuthError(“Must be admin to delete”)
if not store.has_product(prod_id):
raise ValueError(“Unknown product id”)
store.get_product(prod_id).delete()CopyCOPY
2. 要注意 Assert 的语法,加括号可能会出现问题
你会注意到在我们上面的语法和代码中,Assert 后面是没有跟括号的,因为 Assert 是一个 keyword 但不是一个 function。

Assert 语法:

assert_stmt ::= “assert” expression1 [“,” expression2]CopyCOPY
所以如果你写出:

assert(12, “something is wrong”)CopyCOPY
这样的代码,对 Python Interpreter 来讲,它会将整个"(1
2, “Something is wrong”)"当成 expression1 来处理,而这个 “(1==2, “Something is wrong”)” 对 Python 来讲是一个 tuple,而 Non-Empty Tuple 是 True。

比如你可以尝试:

if (1==2, “something is wrong”):
print(“yes”)CopyCOPY
输出的结果是 yes。

所以就会导致这段代码永远为 True,也就是说这个 Assert 是无效的:

assert(1==2, “something is wrong”)CopyCOPY
不过这里需要注意,如果你写的是

assert(12)CopyCOPY
Python是会将其当作 "assert 1
2" 的,因为只有括号围住的部分只有一个 item,并不会将其当成 tuple,除非你给它加一个逗号。也就是:

assert(1==2,)CopyCOPY
那看到这里你可能会想问了,如果我需要写一个跨行的 assert,要怎么办?你可以在每行末尾加个"":

a = 1
assert a == 2,
“a should be 2, it is " + str(a)
+”. Please fix it."CopyCOPY
输出就会是:

Traceback (most recent call last):
File “d:\Python\assert.py”, line 30, in
assert a == 2,
AssertionError: a should be 2, it is 1. Please fix it.

python知识点总结
https://docs.python.org/zh-cn/3/tutorial/index.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值