文章目录
函数和函数式编程
函数
参数传递
不可变对象和可变对象
-
对于不可变对象来说,实参和被修改后的形参是两个对象。**对于变量的重新的赋值操作,相当于重新生成了一个新的对象,**类似于值传递。
- 数值类型,tuple类型,string类型**等都是不可变对象
def fun(arg): print('传入参数的id:{}'.format(id(arg))) arg = 1 # 修改参数的值 print('修改后参数的id:{}'.format(id(arg))) return arg a = 2 print('函数被调用之前的实参的id:{}'.format(id(a))) res = fun(a) print('函数被调用之后的实参的id:{}'.format(id(a))) print('函数返回的形参的id:{}'.format(id(res))) # 结果 函数被调用之前的实参的id:1901637888 传入参数的id:1901637888 修改后参数的id:1901637856 函数被调用之后的实参的id:1901637888 函数返回的形参的id:1901637856
-
对于可变对象来说,实参和被修改后的形参是一个对象,类似于引用传递
- 列表list,字典dict等都是可变对象。
def fun(arg): print('传入参数的id:{}'.format(id(arg))) arg.append(1) # 修改参数的值 print('修改后参数的id:{}'.format(id(arg))) return arg a = [2] print('函数被调用之前的实参的id:{}'.format(id(a))) res = fun(a) print('函数被调用之后的实参的id:{}'.format(id(a))) print('函数返回的形参的id:{}'.format(id(res))) # 结果 函数被调用之前的实参的id:2180016515784 传入参数的id:2180016515784 修改后参数的id:2180016515784 函数被调用之后的实参的id:2180016515784 函数返回的形参的id:2180016515784
-
结论:对于不可变对象,如果进行重新指向,则是生成实参和形参是两个对象;对于可变对象,形参和实参是同一个对象。
参数
必须参数
- 略
关键字参数
- 略
默认参数
- 略
不定长参数
封包与解包
封包
-
将多个值付给一个变量时,python会自动将这些值封装成元组,这个特性称为封包
a = 1, 2, 'a' print(a, type(a)) # 结果 (1, 2, 'a') <class 'tuple'>
-
当函数有多个返回值是,封包
def fun(): return 1, 2, 'a' res = fun() print(res, type(res)) # 结果 (1, 2, 'a') <class 'tuple'>
解包
-
接受函数返回值
def test(): return 1, 2, 3 a, b, c = test() print(a, b, c) # 结果 1 2 3
def test(): return 1, 2, 3 a, *b = test() print(a, b) # 结果 1 [2, 3]
-
传递参数
def fun1(*args): print(type(args), args) a = (2, 4, 6) func1(*a) # 将元组解包成可变参数 # 结果 <class 'tuple'> (2, 4, 6)
def fun2(**kwargs): print(type(kwargs), kwargs) b = {'一': 1, '二': 2} fun2(**b) # 将字典解包成关键字参数 # 结果 <class 'dict'> {'一': 1, '二': 2}
带一个星号*
-
一般在函数定义时,用 *args 表示。
- 此时args定义在函数的中的数据类型是元组tuple类型。
def fun(*args): print(type(args), args) # 方式一 fun(2, 4, 6) # 方式二 a = (2, 4, 6) fun(*a) # 将元组解包成可变参数 # 结果 <class 'tuple'> (2, 4, 6) <class 'tuple'> (2, 4, 6)
带两个星号**
-
一般在函数定义时,用 **kwargs表示
- 此时kwargs定义咋函数中的数据类型是字典类型
def fun(**kwargs): print(type(kwargs), kwargs) # 方式一 fun(a=1, b=2) # 方式二 b = {'a': 1, 'b': 2} fun(**b) # 将字典解包成关键字参数 # 结果 <class 'dict'> {'a': 1, 'b': 2} <class 'dict'> {'a': 1, 'b': 2}
函数式编程
- 重要特征:(1)将函数作为形参传入,(2)将函数作为返回值返回
返回函数
闭包
def outerFun(*args): # 定义外部函数,并接受不定长参数
def innerFun(): # 定义内部函数
ax = 0 # 定义局部变量
for n in args: # 使用enclosing
ax = ax + n
return ax
return innerFun # 返回内部函数
fun = outerFun(1, 2) # 使用返回的函数
res = fun() # 将返回的函数调用,并将函数的运行结果赋值给res
print(res)
# 结果
3
在函数
outerFun
中又定义了函数innerFun
,并且,内部函数innerFun
可以引用外部函数outerFun
的参数和局部变量args
,当outerFun
返回函数innerFun
时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”
- 注意点:返回时,写的是内部函数的函数名,而不是返回内部函数的调用。
装饰器 decorator
-
由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。
-
基本形式,无参
# 定义一个需要被包装的函数 def need_decorated_fun(num): print(num) # 定义一个装饰器 def decorated(fun): # 装饰器的参数是一个被装饰函数的名称 def wrapper(*args, **kwargs): # 具体的装饰实现,使用wrapper,接受不定长参数,封包 print('function name:%s' % fun.__name__) # 打印被装饰函数的__name__属性, # 函数增强 return fun(*args, **kwargs) # 返回被装饰函数的运行结果,解包 return wrapper need_decorated_fun = decorated(need_decorated_fun) need_decorated_fun(2) print('修改后原需要被装饰函数的函数名为:%s' % need_decorated_fun.__name__) # 结果 function name:need_decorated_fun 2 修改后原需要被装饰函数的函数名为:wrapper
- 借助
@
符号,无参
# 定义一个装饰器 def decorated(fun): # 装饰器的参数是一个被装饰函数的名称 def wrapper(*args, **kwargs): # 具体的装饰实现,使用wrapper,接受不定长参数,封包 print('function name:%s' % fun.__name__) # 打印被装饰函数的__name__属性,函数增强 return fun(*args, **kwargs) # 返回被装饰函数的运行结果,解包 return wrapper # 使用@符号进行装饰需要被装饰的函数 @decorated def need_decorated_fun(num): print(num) need_decorated_fun(112) print('修改后原需要被装饰函数的函数名为:%s' % need_decorated_fun.__name__) # 结果 function name:need_decorated_fun 112 修改后原需要被装饰函数的函数名为:wrapper
@decorated
放在need_decorated_fun
之上,相当于need_decorated_fun = decorated(need_decorated_fun)
- 借助
-
基本形式,有参
# 定义一个需要被包装的函数 def need_decorated_fun(num): print(num) # 定义一个装饰器,含参数 def para_decorated(para): # para表示需要额外传入的参数 def decorated(fun): #装饰器的参数是一个被装饰函数的名称 def wrapper(*args, **kwargs): # 具体的装饰实现,使用wrapper,接受不定长参数,封包 print('additional parameter:%s' % para) print('function name:%s' % fun.__name__) # 打印被装饰函数的__name__属性, # 函数增强 return fun(*args, **kwargs) # 返回被装饰函数的运行结果,解包 return wrapper return decorated need_decorated_fun = para_decorated('Para Decorated')(need_decorated_fun) need_decorated_fun(112) print('修改后原需要被装饰函数的函数名为:%s' % need_decorated_fun.__name__) # 结果 additional parameter:Para Decorated function name:need_decorated_fun 112 修改后原需要被装饰函数的函数名为:wrapper
-
借助
@
符号,有参# 定义一个装饰器,含参数 def para_decorated(para): # para表示需要额外传入的参数 def decorated(fun): #装饰器的参数是一个被装饰函数的名称 def wrapper(*args, **kwargs): #具体装饰实现,用wrapper,接受不定长参数,封包 print('additional parameter:%s' % para) print('function name:%s' % fun.__name__) # 打印被装饰函数__name__属性 # 函数增强 return fun(*args, **kwargs) # 返回被装饰函数的运行结果,解包 return wrapper return decorated @para_decorated('Para Decorated') def need_decorated_fun(num): print(num) need_decorated_fun(112) print('修改后原需要被装饰函数的函数名为:%s' % need_decorated_fun.__name__) # 结果 additional parameter:Para Decorated function name:need_decorated_fun 112 修改后原需要被装饰函数的函数名为:wrapper
@para_decorated('Para Decorated')
放在need_decorated_fun
之前,相当于need_decorated_fun = para_decorated('Para Decorated')(need_decorated_fun)
-
-
存在问题:修改后原需要被装饰函数的函数名为:wrapper,我们希望即使需要被装饰的函数在装饰完毕后仍具有原来的函数名
- 方式一:把原始函数的
__name__
等属性复制到wrapper()
函数中,编写wrapper.__name__ = func.__name__
这样的代码 - 方式二:使用
@functools.wraps(被装饰函数名)
,在functools
模块中
在不需要现实定义函数的时候,传入匿名函数更方便
- 方式一:把原始函数的
装饰器完整形式,无参
import functools
# 定义一个装饰器
def decorated(fun): # 装饰器的参数是一个被装饰函数的名称
@functools.warps(fun) # 保证被装饰的函数的函数名不变
def wrapper(*args, **kwargs): # 具体的装饰实现,使用wrapper,接受不定长参数,封包
print('function name:%s' % fun.__name__) # 打印被装饰函数的__name__属性,函数增强
return fun(*args, **kwargs) # 返回被装饰函数的运行结果,解包
return wrapper
# 使用@符号进行装饰需要被装饰的函数
@decorated
def need_decorated_fun(num):
print(num)
need_decorated_fun(112)
print('修改后原需要被装饰函数的函数名为:%s' % need_decorated_fun.__name__)
# 结果
function name:need_decorated_fun
112
修改后原需要被装饰函数的函数名为:need_decorated_fun
装饰器完整形式,有参
import functools
# 定义一个装饰器,含参数
def para_decorated(para): # para表示需要额外传入的参数
def decorated(fun): #装饰器的参数是一个被装饰函数的名称
@functools.warps(fun) # 保证被装饰的函数的函数名不变
def wrapper(*args, **kwargs): #具体装饰实现,用wrapper,接受不定长参数,封包
print('additional parameter:%s' % para)
print('function name:%s' % fun.__name__) # 打印被装饰函数__name__属性 # 函数增强
return fun(*args, **kwargs) # 返回被装饰函数的运行结果,解包
return wrapper
return decorated
@para_decorated('Para Decorated')
def need_decorated_fun(num):
print(num)
need_decorated_fun(112)
print('修改后原需要被装饰函数的函数名为:%s' % need_decorated_fun.__name__)
# 结果
additional parameter:Para Decorated
function name:need_decorated_fun
112
修改后原需要被装饰函数的函数名为:need_decorated_fun
匿名函数
- 格式:
lambda 参数列表:表达式
,函数的返回值是表达式的值 - 本质:匿名函数本质上就是个函数,完全可以用显示定义的方式给出
f = lambda x:x*x # 将一个匿名函数赋值给一个对象
res = f(3) # 调用函数
print(res)
# 结果
9
- 匿名函数,在一些高阶函数,或其他框架的函数会被经常性的使用
高阶函数
- 变量可以指向函数;函数名也是变量;所以变量可以指向函数名
map
-
map(函数(仅含有一个参数),Iterable对象)
,返回值为Iterator
-
其中传入的
函数
,含有一个参数,对应于Iterator
中的每一个元素 -
因为Iterator是惰性序列,可以通过list()函数将整个序列都计算出来,并返回一个list
def fun(x): return 2 * x res1 = map(fun, [1, 2]) print(type(res1)) print(res1) res2 = list(res1) #通过list()函数将整个序列都计算出来,并返回一个list print(res2) # 结果 <class 'map'> <map object at 0x000002719C6DD278> [2, 4]
-
使用匿名函数
res1 = map(lambda x: 2 * x, [1, 2]) print(type(res1)) print(res1) res2 = list(res1) #通过list()函数将整个序列都计算出来,并返回一个list print(res2) # 结果 <class 'map'> <map object at 0x0000018901D5C7B8> [2, 4]
-
reduce
-
reduce(函数(仅含有两个参数),Iterable)
-
reduce
把一个函数作用在一个序列[x1, x2, x3, ...]
上,这个函数必须接收两个参数,reduce
把结果继续和序列的下一个元素做累积计算 -
返回值是
函数
的返回值,并不是一个Iterator
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
-
reduce
在funtools
模块中from functools import reduce def fun(x, y): return x + y res1 = reduce(fun, [1, 2, 3, 4]) print(type(res1)) print(res1) # 结果 <class 'int'> 10
-
使用匿名函数
from functools import reduce res1 = reduce(lambda x,y: x - y, [1, 2, 3, 4]) print(type(res1)) print(res1) # 结果 <class 'int'> -8
-
filter
-
filter
用于过滤序列,其中满足条件返回True
的元素被保留下来,不满足条件返回False
的元素不被保留 -
filter(函数(仅含一个参数), Iterable)
-
其中
函数
仅含一个参数,对应Iterable
中的每一个参数 -
最终的返回值是一个
Iterator
,可以通过list()函数将整个序列都计算出来,并返回一个listdef isOdd(x): return x % 2 == 1 res1 = filter(isOdd, [1, 2, 3, 4])#[1, 2, 3, 4] →isOdd→ [True, False, True, Flase] #满足条件对应True位置上的元素被保留下来 print(type(res1)) print(res1) res2 = list(res1) # #通过list()函数将整个惰性序列都计算出来,并返回一个list print(res2) # 结果 <class 'filter'> <filter object at 0x000001A98A27D278> [1, 3]
sorted
-
排序的核心是比较两个元素的大小。如果是数字,我们可以直接比较;
-
如果是字符串或者两个dict呢?直接比较数学上的大小是没有意义的,因此,比较的过程必须通过函数抽象出来
-
sorted(Iterable, key, reverse=False)
,关键就是如何写这个key
;其中Iterable
中的每个元素之间都是应该可比较的。- 如数值
1
和字符串'1'
之间不可比较,此时运用在key中的比较,会产生TypeError异常
- 如数值
-
最终的返回结果是列表
list
类型,而不是Iterator
-
默认情况
res = sorted([1, 3, 2]) # 数值默认按数值由小到大排序 print(res) # 结果 [1, 2, 3]
res = sorted(['1', 'a', 'A']) # 字符串默认按ASCII码由小到大排序 print(res) # 结果 ['1', 'A', 'a']
-
按照key对应的函数规则进行排序
key
指向的函数
中仅包含一个参数时,该参数对应Iterable
中的每个第一级元素
res = sorted([6, 5, -1, 9, -11], key=abs) #在函数abs的作用下,各元素的权重为 #[6, 5, 1, 9, 11],对应权重的权重越大,越靠后 print(res) # 结果 [-1, 5, 6, 9, -11]
res = sorted([6, 5, -1, 9, -11], key=lambda x: -x) #对应的权重变为[-6, -5, 1, -9, 11], #然后按照权重排序,权重越大,越靠后 print(res) # 结果 [9, 6, 5, -1, -11]
-
字典按照
key
或value
排序-
字典按照
key
排序my_dict = {"k2": 5, "k3": 6, "k1": 4, "k0": 3} new_list = sorted(my_dict.items(), key=lambda x:x[1]) # my_dict.items()将键值对转为可迭代的对象,[(键,值),...] # x表示items中的一个元素, x[0]表示按第一个位置key排序 print(my_dict) print(my_dict.items()) print(new_list) # 结果 {'k2': 5, 'k3': 6, 'k1': 4, 'k0': 3} dict_items([('k2', 5), ('k3', 6), ('k1', 4), ('k0', 3)]) [('k0', 3), ('k1', 4), ('k2', 5), ('k3', 6)]
-
字典按照
value
排序my_dict = {"k2": 5, "k3": 6, "k1": 4, "k0": 3} new_list = sorted(my_dict.items(), key=lambda x:x[1]) # my_dict.items()将键值对转为可迭代的对象,[(键,值),...] # x表示items中的一个元素, x[1]表示按第二个位置value排序 print(my_dict) print(my_dict.items()) print(new_list) # 结果 {'k2': 5, 'k3': 6, 'k1': 4, 'k0': 3} dict_items([('k2', 5), ('k3', 6), ('k1', 4), ('k0', 3)]) [('k0', 3), ('k1', 4), ('k2', 5), ('k3', 6)]
-
-
偏函数
-
偏函数所在模块
functools
a1 = int('123') #示123在十2进制下对应的十进制的数,1*10^2+2*10^1+3*10^0=123 a2 = int('123', base=8) #表示123在8进制下对应的十进制的数,1*8^2+2*8^1+3*8^0=83 print(a1) print(a2) # 结果 123 83
-
自定义个函数
def int2(x, base=2): return int(x, base) #返回一个x在2进制下表示的十进制的数 res = int2('1001') print(res) # 结果 9
-
使用偏函数
import functools int2 = functools.partial(int, base=2) res = int2('1001') print(res) # 结果 9
变量访问顺序:LEGB
-
LEGB其他python解释器查找变量的顺序:LEGB
-
Local:函数内部作用域
-
Enclosing function:内嵌函数对函数内部变量的应用,即闭包(闭合函数)
-
Global:全局作用域
-
Build-in:python解释器默认导入的一些list/tuple等
-