第六章 Python 函数(二)
一、一等公民:函数
在 Python 中对象的优先等级:
函数 ---> 类 ---> 模块 ---> 包
1. 函数即"变量"
在这里有必要强调一点:在 Python 中一切皆对象的概念;包括数字、字符串、元组、列表、字典和函数
对就是,函数也是对象,还有列表、字典和元组中的一个元素都是对象。
也就是,当你去定义一个对象是,每个对象都是有他的内存地址的
>>> a = 'hlep' >>> id(a) 140122643069728 >>> for i in a: ... id(i) ... 140122643280984 140122643550816 140122643550480 140122643175720 # 现在定义一个函数 >>> def f1(): ... print('只有调用我时,此处才会被执行') ... # 查看它的内存地址 >>> id(f1) 140122643775000 >>>
既然函数也是和其他的对象一样,那么它本身也应该具有某些对象的属性和特点。比如 定义一个列表:
# 这里list1 是一个变量变量名 >>> list1 = ['a','b','c'] # 变量名也可以又被赋值给其他变量名 >>> list2 = list1 >>> list1 is list2 True >>> id(list1) 140122643092424 >>> id(list2) 140122643092424 >>> # 当然函数也可以这样操作 >>> def f1(): ... print('当这个函数被调用时,此处代码才会被执行') ... # 可以将此函数赋值给其他的变量名 >>> func = f1 >>> print(f1) <function f1 at 0x7f70d85d8510> >>> print(func) <function f1 at 0x7f70d85d8510> >>> func is f1 True >>> 从以上信息可以看出 func 就是 f1,那么此时调用 func() 就是在调用f1() >>> func() 当这个函数被调用时,此处代码才会被执行 >>>
当定义一个函数,其实是执行了对一个变量名(即函数名)赋值的操作。
>>> def f1(): ... print("其实这里的函数体,在定义函数时,在内存里就是一串字符串") ... >>> >>> # 在内存中是这样的:f1 = 'print("其实这里的函数体,在定义函数时,在内存里就是一串字 符串")' '''只有在调用时,python 会对这串字符串编译成字节码,再由 python 去解释器这些字节码, ... 解释字节码是,会先检查字节码中那些是符合 python 的语法,再跟进语法执行。'''
2. 命名空间和作用域
1 [root@localhost python3]# cat namespace.py 2 #!/usr/bin/env python3 3 # 这个脚本是用来展示命名空间和变量的作用域的 4 x = 1 5 print('主程序 x 的值:',x) 6 print('主程序 x 的内存地址:',id(x)) 7 8 def f1(): 9 x = 10 10 print('函数 x 的值:',x) 11 print('函数 x 的内存地址:',id(x)) 12 13 f1() 14 [root@localhost python3]# python3 namespace.py 15 主程序 x 的值: 1 16 主程序 x 的内存地址: 9220192 17 函数 x 的值: 10 18 函数 x 的内存地址: 9220480 19 [root@localhost python3]# 20 21 #
二、高阶函数
特点:
-
1. 把一个函数的函数名当做实参传给另一个函数
前面提到了, 函数即变量,定义一个函数,也就是定义一个变量而已。变量可以被当做参数来被函数使用,当然,函数也可以被当做参数被传递和使用
# 定义一个函数,打印 数字 521 >>> def answer(): ... print(521) ... # 再定义一个函数,这个函数有个形参 func >>> def run_something(func): ... func() # 调用被传进来的函数 ... # 执行run_something函数时,把 answer函数的函数名当做一个实参传给这个函数 >>> run_something(answer) 521 >>> # 传的时候可不是传 answer() 而是 answer 函数的本身 >>> def run_something(func): ... print(func) ... # 传的是 answer(),那么这个函数将会先运行,之后传进去的只是函数的执行的结果,这样, run_something 这个函数 # 接收到的将是一个 None ,之后就会报错 >>> run_something(answer()) ok # 函数先在解释器中运行了本身 None # 之后输出了返回值 Traceback (most recent call last): # 传递个使用它的函数时,传的是 None ,所以报错了 File "<stdin>", line 1, in <module> File "<stdin>", line 3, in run_something TypeError: 'NoneType' object is not callable >>>
再来看另一例子:
1 >>> def add_args(a,b): 2 ... print(a + b) 3 ... 4 >>> def run_somthing_with_args(func,arg1,arg2): 5 ... func(arg1,arg2) 6 ... 7 >>> run_somthing_with_args(add_args,5,6) 8 11 9 >>> 10 # 运行run_somthing_with_args(5,6,add_args)时: 11 5,6被赋值给 arg1,arg2;add_args被赋值给了 func; 12 而在函数 run_somthing_with_args 中,调用含参数的函数func(arg1,arg2), 13 而在此时,函数func 也就是add_args,而这两个参数arg1,arg2也就传给了 a 和 b, 14 即 a = arg1 = 5,b = arg2 = 6 15 在这里同样可以用上 *args,**kwargs 的技巧 16 >>> def sum_args(*args,**kwargs): 17 ... print(args,kwargs) 18 ... 19 >>> def run_with_args(func,*args,**kwargs): 20 ... func(*args,**kwargs) 21 ... 22 >>> run_with_args(sum_args,2,a=1) 23 (2,) {'a': 1} 24 >>>
-
2. 另一个函数返回值中包含一个函数的函数名
函数可以返回任意数量的任何类型的数据,当然也包括某一个函数的本身
>>> def sum_args(*args): ... print(sum(args)) ... >>> def return_func(func): ... return func ... >>> return_func(sum_args) <function sum_args at 0x7f0eaeae0e18> #可以看出执行函数,此时返回的只是一个对象,对象的名字就叫 sum_args >>> f1 = return_func(sum_args) # 把这个对象赋值个一个变量名,即 f1 = sum_args >>> f1(7,8) #调用f1(7,8) 也就是在调用 sum_args(7,8) 15 >>>
三、嵌套函数
嵌套函数有时也叫做内部函数,就是在一个函数的内部再定义一个函数
>>> def outer(a,b): ... def inner(c,d): ... return c + d ... return inner(a,b) ... >>> outer(5,6) 11 >>> # 来简单分析一下执行 outer(5,6)时的流程: 1. 执行 outer(5,6), 将5和6 传入函数outer 即:a = 5, b = 6 2. 在函数 outer 内部定义另一个函数 inner 即: inner = 'return c + b' 3. 最后返回一个值,这个值就是函数 inner的结果,同时把变量 a 和 b 当做实参传给了 inner 函数, 即:c = a = 5 , d = b = 6 在 inner 函数内部也有个返回值 c + d ,此时也就是 5 + 6 4. 执行的结果就是 outer(5,6) = inner(5,6),结果就是 11
四、闭包函数
闭包函数是另一个函数动态生成的函数,并且可以储存和使用或者改变闭包函数外创建的变量的值
>>> def outer(arg): ... def inner(): ... return '我是内部的闭包函数,我在使用外部的变量:%s' % arg ... return inner ... >>> outer("Im's outer arg") <function outer.<locals>.inner at 0x7f0eaea35730> >>> f1 = outer("Im's outer arg") >>> f1() "我是内部的闭包函数,我在使用外部的变量:Im's outer arg" >>>
函数 inner 使用了其外部函数的参数(变量) arg ,并且外部函数返回的是函数 inner 本身,而不是调用函数 inner 和函数inner的返回值
此时, inner函数就是一个闭包函数
>>> f1
<function outer.<locals>.inner at 0x7f0eaea35950>
>>>
五、装饰器
装饰器中会会使用一下几个技巧
· *args 和 ** kwargs
· 闭包
· 作为参数的函数
-
1. 基本装饰器
import time def inner(): time.sleep(3) print('in then inner') def timer(func): def wraper(): start_time = time.time() func() stop_time = time.time() print('func run time id %s' %(stop_time - start_time)) return wraper decorator = timer(inner) # 手工设置装饰器 decorator() 执行结果: in then inner func run time id 3.000171422958374 # 上面是手工赋值的方式;当然Python不会这么low,已经给你制定了一个很好的方试, 让你可以足够的装逼 import time def timer(func): def wrapper(): start_time = time.time() func() stop_time = time.time() print('func run time id %s' %(stop_time - start_time)) return wrapper @timer # 这里的 @timer 就是Python提供的装逼技巧
def inner(): time.sleep(3) print('in then inner') inner()
装饰器做了两件事:
1. 执行装饰器函数,并且同时把要装饰的函数名,当做实参传入。
2. 返回一个在装饰器内部定义的函数名,即一个对象;并且这个返回值含有了两个东西:
a. 内部函数本身的功能
b. 被装饰的函数执行结果
当使用装饰器时又做了两件事:
1. 执行装饰器函数
2. 把装饰器的返回值又重新赋值给被装饰的函数;即 inner = wrapper
-
2. 装饰器执行流程
-
3. 装饰器之传参
-
4. 装饰器之返回值
五、多层装饰器
六、内置函数
内置函数也叫内置方法,在 Python 已启动就加载到 Python 解释器里的。可以直接使用的一些函数。
下图中就是 Python 中全部的内置函数
值得注意的几个内置函数用法
#compile f = open("函数递归.py") data =compile(f.read(),'','exec') exec(data) #print msg = "又回到最初的起点" f = open("tofile","w") print(msg,"记忆中你青涩的脸",sep="|",end="",file=f) # #slice # a = range(20) # pattern = slice(3,8,2) # for i in a[pattern]: #等于a[3:8:2] # print(i) # # #memoryview #usage: #>>> memoryview(b'abcd') #<memory at 0x104069648> #在进行切片并赋值数据时,不需要重新copy原列表数据,可以直接映射原数据内存, import time for n in (100000, 200000, 300000, 400000): data = b'x'*n start = time.time() b = data while b: b = b[1:] print('bytes', n, time.time()-start) for n in (100000, 200000, 300000, 400000): data = b'x'*n start = time.time() b = memoryview(data) while b: b = b[1:] print('memoryview', n, time.time()-start)