Python核心技术与实战——二十|assert的合理利用
我们平时在看代码的时候,或多或少会看到过assert的存在,并且在有些code review也可以通过增加assert来使代码更加健壮。但是即便如此,assert还是很容易被人忽略,可是这个很不起眼的用法,如果用的得当的话,会对我们的代码大有裨益。所以,我们今天就来看一看assert的用法。
什么是assert?
Python的assert可以被看做是一个debug的工具,主要测试一个条件是否满足,如果测试的条件满足,则什么也不执行,相当执行了pass语句;而如果条件不符合,则会抛出AssertionError,并返回具体的错误信息(optional)。他的具体语法是这样的
assert_stmt ::='assert' expression [',',Exception]
我们看看一个简单形式的assert expression的例子:
assert 1 == 2
就相当于下面的两行代码:
if __debug__:
if not expression : raise AssertionError
再开看看另外一种格式
assert 1 == 2,'assertion is wrong'
就相当于下面的两行代码格式
if __debut__:
if not expression1:raise AssertionError(expression2)
这里的__debug__是一个常数,如果Python程序执行时候带了-o这个选项,比如
Python -O test.py
那么,程序中所有assert语句都会失效,常数__debug__就为False,反之则为True。
不过要注意的是,直接对常数赋值是非法的,因为他的值在解释器运行的时候已经决定了,中途是无法改变的。
另外还要记住一点:在使用assert的时候一定不要加括号,比如下面的形式
assert (1 == 2,'this should fail')
因为这样写的话,无论表达式是否正确,assert检查都不会是fail,程序只会给出SyntaxWarning的消息,正确的写法是这样的
assert 1 ==2 ,'this should fail'
##########输出##########
AssertionError: this should fail
总的来说,asssert在程序中的作用,是对代码做一些internal的self-check。使用assert,就表示很确定这个条件一定会发生或者一定不会发生。
举个例子,比如这里有一个函数需要给定的参数是人的性别,而正常情况性别之分男和女,我们就可以使用assert来防止程序的非法输入。如果程序没有bug那么assert永远不会抛出异常,而一旦抛出异常就很容易定位程序是在哪里出了问题,便于定位。
assert的用法
讲完了assert的基本语法和概念,我们下面通过一些实际的场景来看看assert在Python中的用法,并弄清楚assert的使用场景。
第一个例子。我们假设超时在做促销活动,准备对一些商品进行打折,那么后台就需要做一个apply_discount()的函数,要求输入为原来的价格和折扣,输出是折扣以后的价格,那么,我们就可以大概写成下面的样子:
def apply_discount(price,discount):
update_price = price*(1-discount)
assert 0 < update_price<price,'price should be greated or equal to 0'
return update_price
然后我们可以通过给定几组数来验证一下他的功能
print(apply_discount(100,0.8))
##########输出##########
19.999999999999996 print(apply_discount(100,2))
##########输出##########
AssertionError: price should be greated or equal to 0
可以看出来,如果discount是0.2的时候,输出是正常的,但是discount如果成了2时,程序就会抛出异常了。这个时候,开发人员修改相关代码或增加进新功能的时候,在运行测试的时候非常容易看出问题。所以,assert的加入,可以有效的预防bug的发生,提高程序的健壮性。
第二个例子,最常见的除法操作是在哪个领域都会遇到的,比方我们想知道一批货物的销售的平均价格,就要给定销售总额还有销售数,这样平均售价就能算出来。
def calculate_average_price(totle_sales,num_sales):
assert num_sales > 0,'number of sales should be greater than 0'
return totle_sales/num_sales
同样我们在函数里增加了assert语句,规定了销售的总数量必须大于0,这样就可以防止后台计算错误。
除了上面两个例子,在实际工作中,assert还有一些很常见的用法,比方下面的场景:
def fun(input):
assert isinstance(input,list),'input must be type of list'
if len(input) == 1:
pass
elif len(input) == 2:
pass
else:
pass
函数里的操作是基于input是个list这个前提,我们函数开始的地方加一个assert检查,防止程序出错。
但是要注意的是,在这个函数中加了assert的前提是我们十分确定程序的输入是一个list,而不能是其他的数据类型。如果我们的这个函数是个多态的,针对不同的数据类型有不同的操作,那就应该写成if...else...的条件语句了
def fun(input):
if isinstance(input,int):
pass
elif isinstance(input,str):
pass
else:
pass
assert的错误示例
通过前面讲的assert的使用场景,有可能会让我们比较迷茫——很多地方都可以使用assert,那么很多if...else的条件语句是不是也可以换成assert呢?这种想法可能就不准确了。接下来,我们看一看几个典型的错误用法:
比方我们要删除一些数据,但是删除操作必须是admin用户才可以,那么就有了下面的代码,
def delete_data(user,data_id):
assert user_is_admin(user),'user must admin'
assert data_exist(data_id),'data must exist'
delete(data_id)
那么上面的代码有什么问题么?
assert的检查是可以被关闭的,在运行Python程序时候,加入一个-O选项就会使assert失效。因此,一旦assert的检查被关闭,user_is_admin()和course_exist()这两个函数就不会执行,就会导致下面的问题:
1.任何用户都可以删除数据;
2.不管数据是否存在,都可以墙纸执行删除操作
这就会给程序带来巨大的安全漏洞,正确的做法,是使用条件语句进行相应的检查,然后抛出相关的异常信息。
def delete_data(user,data_id):
if not user_is_admin(user):
raise Exception('user must be admin')
if not data_exist(data_id):
raise Exception('data must exist')
delete(data_id)
再来看一个例子,我们想打开一个文件,进行数据的处理,读取等一系列操作,那么下面的写法也是有风险的
def read_and_process(path):
assert file_exist(path),'file must exist'
with opne(path) as f:
pass
因为assert的使用,表明强行指定文件必须存在,但事实情况下,这个假设是不成立的,另外,打开文件操作也可能触发别的异常。所以正确的做法是用try...except来解决
def read_and_process(path):
try:
with open(path) as f:
pass
except Exception as e:
pass
总得来说,assert是不实用于run-time error的检查,比方试图打开一个文件,但文件不存在;或者想要从网上down一个文件,但是中途断网了等。这些情况下我们一般使用错误和异常处理。
总结
我们今天学习了assert的用法——assert通常用来对代码进行必要的self-check,表明我们在写代码的时候很确定这种情况一定会发生,或者一定不会发生。需要注意的是,使用assert的时候,一定不能加括号,否则无论表达式对与错,assert的检查永远不会fail。另外,程序中的assert,可以通过-O等选项被全局disable。
通过几个场景的应用,我们可以发现assert的合理使用可以增加代码的健壮度,同时也方便了程序出错时开发人员的定位排查。
不过,我们也要注意assert的使用场合,大多数情况下程序中出现的不同情况都是意料之中的,西药我们用不同的方案来处理,这时候条件语句就更加合适,而程序中的一些run-time error,异常处理就更加合适
Python核心技术与实战——二十|assert的合理利用的更多相关文章
- Python核心技术与实战——二十|Python的垃圾回收机制
今天要讲的是Python的垃圾回收机制 众所周知,我们现在的计算机都是图灵架构.图灵架构的本质,就是一条无限长的纸带,对应着我们的存储器.随着寄存器.异失性存储器(内存)和永久性存储器(硬盘)的出现, ...
- Python核心技术与实战——二一|巧用上下文管理器和with语句精简代码
我们在Python中对于with的语句应该是不陌生的,特别是在文件的输入输出操作中,那在具体的使用过程中,是有什么引伸的含义呢?与之密切相关的上下文管理器(context manager)又是什么呢? ...
- kubernetes实战(二十八):Kubernetes一键式资源管理平台Ratel安装及使用
1. Ratel是什么? Ratel是一个Kubernetes资源平台,基于管理Kubernetes的资源开发,可以管理Kubernetes的Deployment.DaemonSet.Stateful ...
- Python核心技术与实战——十九|一起看看Python全局解释器锁GIL
我们在前面的几节课里讲了Python的并发编程的特性,也了解了多线程编程.事实上,Python的多线程有一个非常重要的话题——GIL(Global Interpreter Lock).我们今天就来讲一 ...
- Python核心技术与实战 笔记
基础篇 Jupyter Notebook 优点 整合所有的资源 交互性编程体验 零成本重现结果 实践站点 Jupyter 官方 Google Research 提供的 Colab 环境 安装 运行 列 ...
- Python核心技术与实战——六|异常处理
和其他语言一样,Python中的异常处理是很重要的机制和代码规范. 一.错误与异常 通常来说程序中的错误分为两种,一种是语法错误,另一种是异常.首先要了解错误和异常的区别和联系. 语法错误比较容易理解 ...
- Python核心技术与实战——十二|Python的比较与拷贝
我们在前面已经接触到了很多Python对象比较的例子,例如这样的 a = b = a == b 或者是将一个对象进行拷贝 l1 = [,,,,] l2 = l1 l3 = list(l1) 那么现在试 ...
- Python核心技术与实战——十六|Python协程
我们在上一章将生成器的时候最后写了,在Python2中生成器还扮演了一个重要的角色——实现Python的协程.那什么是协程呢? 协程 协程是实现并发编程的一种方式.提到并发,肯很多人都会想到多线程/多 ...
- Python核心技术与实战——十八|Python并发编程之Asyncio
我们在上一章学习了Python并发编程的一种实现方法——多线程.今天,我们趁热打铁,看看Python并发编程的另一种实现方式——Asyncio.和前面协程的那章不太一样,这节课我们更加注重原理的理解. ...
随机推荐
- 061. Rotate List
题目链接:https://leetcode.com/problems/rotate-list/description/ Example 1: Input: 1->2->3->4-&g ...
- java base64相关
文件转Base64: public static String imgToBase64(InputStream inStream) { byte[] data = null; try { //avai ...
- Spring MVC 异步请求 Callable
对于有的请求业务处理流程可能比较耗时,比如长查询,远程调用等,主线程会被一直占用,而tomcat线程池线程有限,处理量就会下降 servlet3.0以后提供了对异步处理的支持,springmvc封装了 ...
- 【OpenJ_Bailian - 2790】迷宫(bfs)
-->迷宫 Descriptions: 一天Extense在森林里探险的时候不小心走入了一个迷宫,迷宫可以看成是由n * n的格点组成,每个格点只有2种状态,.和#,前者表示可以通行后者表示不 ...
- EM算法分析
参考来源: <机器学习>——周志华 https://www.cnblogs.com/jerrylead/archive/2011/04/06/2006936.html 几个概念 极大似然估 ...
- Go语言入门篇-网络经验
Go语言学习手册 golang*看云 golang圣经 wuYinIO 1.go语言开发中的坑 go新手容易犯的三个致命错误 Golang 需要避免踩的 50 个坑 2.go语言数据类型 map ...
- MSSQL 索引
INCLUDE索引作用:减少 key lookup所带来的性能开销. 效率主要体现在覆盖查询(建的索引为覆盖索引),在查询时把SELECT显示列放在INCLUDE里作为非索引健列,不用于查询只显示在结 ...
- 【有奖征集】报表模板库邀您提反馈,轻松赢取P30!
>>立即参赛 赛事初衷 大数据时代,数据的价值愈发彰显,什么样的报表才能真正帮助业务决策?这几乎是所有信息化建设的企业和个人都在思考的问题. 作为报表领域标杆企业,葡萄城于2017年推出了 ...
- KMP(next数组的更新理解)Codeforces Round #578 (Div. 2)--Compress Words
题目链接:https://codeforc.es/contest/1200/problem/E 题意: 有n串字符串,让你连起来:sample please ease in out ---> ...
- TP5使用phpoffice phpexcel包操作excel(导出)
安装composer(window版本) 安装composer(MAC版本) 安装composer(Linux版本) 在PhpStorm配置 导出excel 1.使用composer安装phpoffi ...