系列
函数的特性
- 函数也是对象
- 一个函数可以作为另一个函数的实参
- 一个函数可以作为另一个函数的返回值
- 一个函数可以嵌套在另一个函数中
- 函数是不支持像java/c#那样重载的
"""
在python中,一切皆为对象,这里可以看出函数也是对象。
"""
def fun(a):
print('hello', a)
f =fun
print(f) # <function fun at 0x000001E5885BC268>
Lambda表达式
"""
当函数体中只有一行return语句的时,函数的定义可以用一个lambda表达式来代替,其语法格式为:Lambda[形式参数1,形式参数2....]
lambda 是一个匿名简化版的函数:
1.没有函数名
2.没有关键字def,但有lambda
3.没有小括号
4.关于形式参数的表达式相当于函数的返回值
"""
print((lambda a, b: a+b)(1, 2)) # 3
result = map(lambda a: a * a, [1, 2, 3, 4])
print(list(result)) # [1, 4, 9, 16]
def do_sth():
return lambda a, b: a+b
print(do_sth()(1, 2)) # 3
偏函数
"""
偏函数可以简化函数的调用
定义函数时,可以给形参设置默认值,从而简化函数的调用,只有与默认值不符的形参才需要传递额外的实参
"""
from functools import partial
def f(a, b=5):
print('a=', a, 'b=', b)
f_new = partial(f, 2)
f_new() # a= 2 b= 5
f_new(6) # a= 2 b= 6
f_new(b=7) # a= 2 b= 7
def f2(a, b=5, *args, **kwargs):
print('a=', a, 'b=', b, 'args=', args, 'kwargs=', kwargs)
f2_new = partial(f2, 1, c=6)
f2_new() # a= 1 b= 5 args= () kwargs= {'c': 6}
f2_new(2, 3) # a= 1 b= 2 args= (3,) kwargs= {'c': 6}
Python中函数不支持重载
"""
函数不支持重载
"""
def fun(a):
print('hello', a)
def fun(a, b):
print(a, b)
fun('a') # TypeError: fun() missing 1 required positional argument: 'b'
fun('a', 'b')
闭包
"""
闭包的构成,必须满足下面三个条件:
1. 函数(外函数)的内部嵌套定义了另外一个函数(内函数)
2. 内函数(inner)引用了外函数(outer)中的变量
3. 函数的返回值是内函数
"""
def outer():
a = 10
b = [3]
def inner():
# a = 11 # 如果在这里修改变量a, 相当于在内函数中新定义了一个变量a, 把外函数中的变量a给屏幕了
# a += 1 # 相当于 a = a+1 ,相当于新定义了变量a,但等号后面的a没有定义,所以抛错UnboundLocalError: local variable 'a' referenced before assignment
# b[0] =5 #这是可以的,因为b是一个可变类型的对象
print(a)
return inner
outer()() # 10
"""
通常情况下,函数调用结束后,函数内定义的变量将不再可用
"""
def do_sth():
temp = 8
print(temp)
do_sth()
# print(temp) # 报错,NameError: name 'temp' is not defined
"""
但是对于闭包而言,在外函数调用结束后,外函数中被内函数引用的变量仍然是可用的,因为外函数中被内函数引用的变量会被绑定到内函数的特殊属性__closure__中
"""
ourter_result = outer()
# (<cell at 0x00000287D8DEB888: int object at 0x00007FFAF2F6E470>,),是一个元组
print(ourter_result.__closure__)
# 10, 这里就是在外函数定义的变量,它被内函数引用后就存在这里。
print(ourter_result.__closure__[0].cell_contents)
"""
通常情况下,不能在内函数中修改外函数中的变量,但如果要修改怎么办呢,使用nonlocal关键字
"""
def out4():
a = 10
def inner4():
nonlocal a # 这里它不会在内函数中创建一个新的变量,而是直接引用外函数的变量
a = 11
print(a)
return inner4
out4()() # 11
变量的作用域
"""
变量的作用域有四种:
1. 局部作用域(local),比如函数里面定义的变量,函数调用结束后就会被销毁
2. 嵌套作用域(enclosing), 每次调用嵌套函数中的外函数时,都会创建一个嵌套作用域,当外函数内定义变量时,
变量的作用域为:从定义变量开始到函数结束。外函数用结束后,其对应的嵌套作用域中的所有变量都会被销毁(闭包除外)。
3. 全局作用域(global),每次创建一个模块的时,都会创建一个全局作用域,全局作用域为:从定义变量开始到模块结束。
4. 内置作用域(build-in),每次启用python解释器时都会自动加载内置模块,从而创建一个内置作用域。
内置模块中的函数(内置函数),可以在程序中直接使用,停止解释后会被销毁。
"""
"""
当在某个作用域中访问变量时,会按照如上LEGB的顺序依次搜索该作用域及其后面的所有作用域,只要找到了则停止搜索,如果没有找到则抛出NameError。
因此,如果不同的作用域中定义了同名的变量,根据LEGB的搜索顺序,前面作用域中的变量会屏蔽掉后面的作用域中定义的同名变量。
"""
id ='Global'
def Outside():
id ='Enclosing'
def inside():
id ='Local'
print(id)
inside()
Outside()
函数装饰器
"""
对于某个函数,如果我们不希望在不改变函数代码的情况下,为该函数增加额外的功能,那就可以使用装饰器
装饰器是一个函数,装饰器接收一个函数作为参数(传入的实参是被装的函数),装饰器的内部嵌套定义另一个函数,
内函数中会引用装饰器的参数,并且装饰器的返回值是内函数。
这样,就构成了一个闭包,为了让内函数接收任意类型的参数,将内函数的形参定义为(*args, **kwargs)。
在函数中,首先完成为被装饰函数添加的功能,然后调用被装饰的函数。
把装饰器应用到被装饰函数的语法为:在被装饰函数的前面添加"@装饰器的函数名"。
在被装饰函数add的前面加上@log后,相当于执行了add = log(add),
首先,被装饰的函数add会作为实参传递给装饰器log,然后返回装饰器的内函数wrapper,
最后,将内函数wrapper赋值给名为add(被装饰的函数名)的变量,
这样,再调用被装饰的函数add时,其实调用的是装饰器的内函数wrapper。
"""
def log(func):
def wrapper(*args, **kwargs):
print('函数%s被调用了' % func.__name__)
return func(*args, **kwargs) # 记住这里传入的参数要带星号
return wrapper
@log # 在这里加上装器,相当于 add = log(add)
def add(sum1, sum2):
print(sum1, sum2)
return sum1+sum2
add(1, 2)
# 结果如下:
# 函数add被调用了
# 1 2
print(add.__name__) #wrapper 这里实际执行得是wrapper内函数
"""
如果希望被装饰函数的特殊属性__name__值为其函数名,而不是装饰器的内函数的函数名,
可以在装饰器的内函数前面添加另外一个装饰器:@functool.wraps(装饰器的参数名)。
其中functools.wraps指得是标准库模块functools中的函数wraps。
"""
import functools
def log2(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print('函数%s被调用了' % func.__name__) # 函数add2被调用了
return func(*args, **kwargs)
return wrapper
@log2
def add2(sum1, sum2):
print(sum1, sum2)
return sum1+sum2
add2(1, 2)
print(add2.__name__) # add2
"""
把装饰器应用到被装饰函数时,还可以传递额外的参数。此时,需要编写一个3层嵌套的装饰器。
对于@log3(2019, 7),相当于执行了 add3 = log2(2019, 7)(add3)。
"""
def log3(month, day):
def decorator(func):
print('现在是%i年%i月' % (month, day))
@functools.wraps(func)
def wrapper(*args, ** kwargs):
print('函数%s被调用了' % func.__name__)
print('现在是%i年%i月' % (month, day))
return func(*args, **kwargs)
return wrapper
return decorator
@log3(2019, 7)
def add3(sum1, sum2):
print(sum1, sum2)
return sum1+sum2
add3(1, 2)
# 结果如下:
# 现在是2019年7月
# 函数add3被调用了
# 现在是2019年7月
# 1 2