python教程(三)·函数进阶(下)
下半部分果然很快到来,这次介绍函数的更高级用法,装饰器!
函数嵌套
先来说说函数嵌套,python中的函数是可以嵌套的,也就是说可以将一个函数放在另一个函数里面,比如:
>>> def outer(name):
... def inner():
... print(name)
... return inner
...
>>> func1 = outer('feather')
>>> func2 = outer('Lee')
>>> func1()
feather
>>> func2()
Lee
>>>
你没看错,我们在函数里面定义了另一个函数,并把这个函数返回了?
返回出来的函数是带着它的所在的作用域的,这就是为什么返回出来的函数仍能访问外层函数的变量name,而且可以看到func1()和func2()结果不一样,这表明每次调用外层函数都会重新定义内层函数,事实也是如此。
装饰器
再来看看下面这个函数,<( ̄︶ ̄)↗:
def func():
for i in range(100000):
pass # pass的意思是什么都不做
这段函数只是空转100000次没什么意义,只是做个例子,假如我们想要运使用这个函数并输出运行了多长时间,我们可以这么做:
import time
def func():
for i in range(100000):
pass # pass的意思是什么都不做
# 获取当前时间戳,时间戳就是当前到1970年所经过的秒数
start = time.time()
func()
end = time.time()
print(end-start)
我们可以在函数前后分别获取时间,两个时间的差就是函数的运行时间,这很简单。可是,如果我们有很多个函数都需要统计时间怎么办?每个函数调用前都加上同样的代码吗?
当然不是,受函数嵌套的启发,我们可以像下面这样定义一个函数:
def count(func):
def inner():
start = time.time()
result = func()
end = time.time()
print(end-start)
return result
return inner()
然后把要计算运行时间的函数作为参数传给count函数,就是把count函数当一个代理人,替我们调用函数。
但是这样的话,调用func函数不能写func(),要写count(func),有点别扭,再改进下:
def count(func):
def inner():
start = time.time()
result = func()
end = time.time()
print(end-start)
return result
return inner # 返回这个内层函数,而不是调用
如果再执行了这样的代码:
func = count(func)
这个什么意思?
其实就是把func这个变量(函数也可理解成一种变量)重新绑定成count里面定义的inner()函数。这个时候我们再执行func()就可以达到效果了,完整代码如下:
import time
def func():
for i in range(100000):
pass # pass的意思是什么都不做
def count(func):
def inner():
# 获取当前时间戳,时间戳就是当前到1970年所经过的秒数
start = time.time()
result = func()
end = time.time()
print(end-start)
return result
return inner
func = count(func)
func()
装饰器的雏形出来了!装饰器就是像count这样的函数,用来给其它函数增加额外的功能,count函数在里面定义了一个inner函数用来包裹(装饰)被装饰的函数func。
不过这仅仅是雏形,并不是真正的装饰器,现在该让真正的装饰器登场了!
import time
def count(func):
def inner():
# 获取当前时间戳,时间戳就是当前到1970年所经过的秒数
start = time.time()
result = func()
end = time.time()
print(end-start)
return result
return inner
@count
def func():
for i in range(100000):
pass # pass的意思是什么都不做
func()
在函数func()的上面多了样东西:@count,这个东西就是装饰器,它的本质等同于func = count(func)。这样以后,每当我们需要为哪个函数的增加输出运行时间的功能,就在这个函数的定义前加上@count。
万能的*和**
如果func函数的参数数量不确定,我们可以使用参数收集和分配参数的技巧,代码如下:
def count(func):
# “百搭”的参数定义
def inner(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs) # 展开参数
end = time.time()
print(end-start)
return result
return inner
这样的count装饰器对于参数数量任意的函数都可以匹配了。
带参数的装饰器
装饰器也可以有参数!
import time
def count(switch):
# switch为True则计时,否则关闭计时功能
def wrapper(func):
def inner(*args, **kwargs):
if switch:
start = time.time()
result = func()
end = time.time()
print(end-start)
return result
else:
return func()
return inner
return wrapper
switch = bool(input('是否计时?(y/n)') == 'y')
@count(switch)
def func():
for i in range(100000):
pass # pass的意思是什么都不做
func()
看完以后是不是有点晕?(@_@;)
我来说说这个运行的过程:首先,程序等待输入,假定输入y,这是switch变量赋值为True,然后遇到@count(switch),这一行相当于调用了count函数,这个函数返回了一个装饰器,就是里面的wrapper,然后对func函数使用这个装饰器。
(°ー°〃)
嗯?我应该说清楚了吧。。。
对!还有一个问题 (°ー°〃)
函数签名
每个函数都有一个属性,叫做函数签名,就是函数的名字,比如print函数的函数签名可以这样获得:
>>> print.__name__
'print'
>>>
一个函数使用了装饰器,表面上还是使用这个函数的名字,实际上真的是这样吗?看下面代码:
>>> def outer(func):
... def inner():
... pass
... return inner
...
>>> @outer
... def func():
... pass
...
>>> func.__name__
'inner'
>>>
函数的签名已经改变了!
可是...
这个能有什么问题?
( ̄︶ ̄)↗ 有些时候我们是需要使用这个函数签名的的,比如:根据输入的函数名字使用对应的函数(根据字符串获取函数的方法在后面的小实例有用到),有些模块也可能使用到了函数签名,如果不注意到这个细节,出错了也找不出什么问题来!
那么怎么解决?
解决这个问题的方法很简单,我们只需使用一个内置模块的装饰器:
>>> import functools
>>> def outer(func):
... @functools.wraps(func)
... def inner():
... pass
... return inner
...
>>> @outer
... def func():
... pass
...
>>> func.__name__
'func'
>>>
代码简洁明了,至于原理,我们奉行 “拿来主义”,只需要知道在用于包裹的函数上方加上@functools.wraps()即可,不必纠结functools.wraps的内部实现
多个装饰器
装饰器可以使用多个,多个装饰器的执行顺序是从下往上的,看下面这个例子:
>>> def wrapper_1(func):
... def inner():
... pass
... print('wrapper_1')
... return inner
...
>>> def wrapper_2(func):
... def inner():
... pass
... print('wrapper_2')
... return inner
...
>>> @wrapper_1
... @wrapper_2
... def func():
... pass
...
wrapper_2
wrapper_1
>>>
这很好理解,我们在包装东西的时候,都是先从最里面的一层开始,一层一层包裹,直到最外层(就像“俄罗斯套娃“),所以先执行@wrapper_2再执行外面的@wrapper_1
与此同时,这个例子中并没有调用func函数,但是却有执行的print语句,这更进一步证明了,@wrapper这样的语句相当于执行了func=wrapper(func),这是在函数定义的时候马上就执行的,而不是调用的时候才执行的,要注意了
到目前,我们对函数的讲解就此结束,内容很多,读者们有得是时间消化了,也为我准备后面小实例提供了准备时间,读者们期待吧!(提示一下,我们将使用微信控制电脑!)
ヾ( ̄▽ ̄)Bye~Bye~
python教程(三)·函数进阶(下)的更多相关文章
- 简明python教程三-----函数
函数通过def关键字定义.def关键字后跟一个函数的表标识符名称,然后跟一对圆括号. 圆括号之中可以包括一些变量名,该行以冒号结尾.接下来是一块语句,它们是函数体. def sayHello(): p ...
- 【python 3】 函数 进阶
函数进阶 1.函数命名空间和作用域 命名空间一共分为三种: 全局命名空间 局部命名空间 内置命名空间 *内置命名空间中存放了python解释器为我们提供的名字:input , print , str ...
- python语法基础-函数-进阶-长期维护
############### 函数的命名空间和作用域 ############## """ # 函数进阶 命名空间和作用域 命名空间 全局命名空间——我们自 ...
- Python基础三. 函数、lambda、filter、map、reduce
一.概述 函数, 就是用一些语句组织起来实现一组特定的功能, 用来重复调用. 函数的作用及意义:最大化的重用代码和最小化的代码冗余以及对流程的分解. Python中有哪些函数: 内建的函数 第三方模块 ...
- python学习总结(函数进阶)
-------------------程序运行原理------------------- 1.模块的内建__name__属性,主模块其值为__main__,导入模块其值为模块名 1.创建时间, ...
- 周末班:Python基础之函数进阶
迭代器和生成器 迭代和可迭代 什么是迭代(iteration)? 如果给定一个list或tuple,我们要想访问其中的某个元素,我们可以通过下标来,如果我们想要访问所有的元素,那我们可以用for循环来 ...
- Python学习之函数进阶
函数的命名空间 著名的python之禅 Beautiful is better than ugly. Explicit is better than implicit. Simple is bette ...
- Python全栈开发之路 【第五篇】:Python基础之函数进阶(装饰器、生成器&迭代器)
本节内容 一.名称空间 又名name space,就是存放名字的地方.举例说明,若变量x=1,1存放于内存中,那名字x存放在哪里呢?名称空间正是存放名字x与1绑定关系的地方. 名称空间共3种,分别如下 ...
- python之路——函数进阶
阅读目录 楔子 命名空间和作用域 函数嵌套及作用域链 函数名的本质 闭包 本章小结 楔子 假如有一个函数,实现返回两个数中的较大值: def my_max(x,y): m = x if x> ...
- python基础之函数进阶
假如有一个函数,实现返回两个数中的较大值: def my_max(x,y): m = x if x>y else y return mbigger = my_max(10,20)print(bi ...
随机推荐
- 多个 Word 文档合并为一个
如果您工作中经常要跟 Word 文档打交道,时不时的您可能需要将多个 Word 文档合并为一个.信息量少的时候,我们可以直接使用复制粘贴.除此之外,还有没有其它办法呢? 借助word2010/2007 ...
- 关于Unity3d的世界空间和本地空间的一点思考
最近才开始学Unity3d,被这个本地空间和世界空间搞得有点晕头转向的.于是花了一点时间随便写了一点代码加深理解.代码如下: public class MoveX : MonoBehaviour { ...
- ppt基本操作
一.概述及作用 ppt是一种表达用户思想的有力工具,无论是介绍一个计划,介绍一个新的产品,或是员工报告,员工培训,只要事先做一个演示文稿,那么阐述的过程就会变得见面,简明和清晰. 二.界面构成 演示文 ...
- ZT android -- 蓝牙 bluetooth (一) 入门
android -- 蓝牙 bluetooth (一) 入门 分类: Android的原生应用分析 2013-05-19 21:44 4543人阅读 评论(37) 收藏 举报 bluetooth4.2 ...
- [BZOJ 4555][Tjoi2016&Heoi2016]求和
题意 给定 $n$ , 求下式的值: $$ f(n)= \sum_{i=0}^n\sum_{j=0}^i\begin{Bmatrix}i\\ j\end{Bmatrix}\times 2^j\time ...
- 原生js模仿jq fadeIn fadeOut效果 兼容IE低版本
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...
- antlr-2.7.6.jar的作用
项目中没有添加antlr-2.7.6.jar,hibernate不会执行hql语句 并且会报NoClassDefFoundError: antlr/ANTLRException错误
- BZOJ 3211 花神游历各国 线段树平方开根
题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=3211 题目大意: 思路: 由于数据范围只有1e9,一个数字x开根号次数超过logx之后 ...
- 以整数元素构成的list中的数字组成最小整数
问题 把一个int型数组中的数字拼成一个串,这个串代表的数字最小. 思路说明 不同角度,对原题理解有所不同.我依照以下的理解方式求解. 对这个问题的理解: 有一个元素是int类型的list: 将上述l ...
- apache2 重启、停止、优雅重启、优雅停止
停止或者重新启动Apache有两种发送信号的方法 第一种方法: 直接使用linux的kill命令向运行中的进程发送信号.你也许你会注意到你的系统里运行着很多httpd进程.但你不应该直接对它们中的任何 ...