d函数
一、函数基础
1、什么是函数?
在一个完整的项目中,某些功能会被反复使用。那么会将某段代码封装成函数,当我们要使用功能的时候直接调用函数即可
函数是可以实现一些特定功能的小方法或是小程序。
优点
- 增加了代码的重复使用率 简化代码结构,增加了代码的复用度(重复使用的程度)
- 减少了代码量 提高了 代码 的可读性
- 提高了代码的维护
2、函数的定义
-
主体结构
使用def关键字 + 函数名():
函数体
实例:
def 函数名([参数]): 函数体
函数名:
使用统一标识符来定义函数名
注意:
- 函数名区分大小写
- 函数可以重名 但是会被覆盖
3、函数调用
-
调用函数
格式:函数名(参数列表)
注意:调用函数必须在定义之后
本质:实参给形参赋值的过程
4、最简单的函数(无参无返回值)
speak() # 报错,函数未定义
def speak():
print('Lucky老师真的帅啊')
speak()
speak()
5、函数的返回值
注意
最后的return 表达式,可以不写,相当于return None
-
return 当函数体遇到return 那么return下面的代码将不会在执行 有return的函数可以将值赋给变量
def demo(): return "hello" #返回单个值 return ["a","b"] #返回多个值 == return 1,2,4#元祖的形式 print("我将不会在输出") var = demo() print(var)
-
如果没有返回值的函数 不能将值赋给变量 当赋值给变量 打印该变量为None
def demo(): print("没有返回值") print(demo()) #None
-
返回多个值
多个值用逗号分隔
#return 返回多个值 def value(): return [1,2,3,4,'a','b'] return 1,2,3 #(1, 2, 3) return {"a":1} return 1, 2 # 多个值用逗号分隔 print(value())
二、函数提升
1、 带普通参数的函数
形参:在函数定义的时候 在括号里 写入的变量称为 形参
实参:在函数调用的时候 传递的参数 成为实参
-
注意:参数的数量理论上是无限的,但是实际上最好不要超过6、7个,类型是没有限制
实例:
def add(x,y): #形参 且没有默认值 print(x,y) add(10,20) #实参 实参的个数要和形参的个数对应上 前提是 形参没有默认值
2、参数默认值
-
主体结构
def 函数名(参数=值,参数=值):
pass
实例:
def test(x=10,y): #函数的形参的默认值 如果在形参中有的参数有默认值 有的没有 那么要将有默认值的参数 放在后面 print(x,y) test() #SyntaxError non-default argument follows default argument
-
注意:
- 如果定义的函数有形参 且 没有默认值 那么在调用的时候 必须传实参 且传的实参的个数 不能大于 形参的个数
- 函数在定义的时候 如果形参都有默认值 那么传的实参 会将默认值 覆盖掉 此时的实参可传可不传
- 函数的形参的默认值 如果在形参中有的参数有默认值 有的没有 那么要将有默认值的参数 放在后面(要遵循默认值的规则)
3、关键字参数
可以使用关键字 来改变我的形参与实参的默认从左到右的对应关系
-
注意:
当不按照顺序给形参的值的时候 关键字 要么就都给 要么就不给
实例
def test(x,y): print('x为',x,'y为',y) test(y=10,x=100) test(10,100)
-
关键字参数和默认值的搭配
def a(x=0,y=0): print(x,y) a(2,y=2) def a(a,b,c): print(a,b,c) print(a(0,1,c=2))
-
注意:
有形参的且没有默认值的时候 必须要有实参
有形参且有默认值的时候 实参可有可无( 默认参数必须放在最后面,否则会报错)
参数的位置可以改变 但是在使用的时候 形参名=实参的值
如果定义的函数 有形参 且没有默认值 则关键字参数 都需要指定给值#如果定义的函数 有形参 且有默认值 则可以只给第一个设置关键字 后面可以不传实参 如果第一个给了关键字 则后面都需要统一给定关键字
-
如果形参后面都有默认值 则可以指定后面的某个参数传递关键字参数
def test(x,y=2,z=3): print(x,y) test(1,z=4)
4、不定长参数
概述: 也就是传入的参数的个数不固定 能处理比当初定义时更多的参数
-
*args
不确定参数 *形参名 以元组的形式去接收
在形参变量前加了个星号,变量会存放所有未命名的变量的参数,如果在函数调用时没有指定参数,它就是个空元组
示例:
def func(*args): print(args) #(1,5345,34) 元组类型 func(1,5345,34) def func(a,b,*args): print(a) #1 print(b) #5 print(args)#(2,34) func(1,5,2,34)
-
**kwargs
不确定实参的**接收 必须是 关键字 加值 接收以后 是 字典类型
def func(**kwargs): #接收不确定的形参的值 以元组的形式 print(kwargs) for i in kwargs: #遍历元组 print(i,kwargs[i]) func2(x=1, y=2, z=3) #函数调用
-
*和 **一起作为形参
*args
和**kwargs
可以同时在函数的定义中,但是*args
必须在**kwargs
前面. SyntaxError: invalid syntaxdef func(*args,**kwargs): print(args,kwargs) func(1,2,3,4,username="lucky",password="123456")
-
** 函数调用的时候的** 和参数**的接收
def func(**dic): print(dic) # {'name':'zs'} x = {'name': 'zs'} func(**x)
-
当调用处实参为*args的情况
def func(a,b,c,d): pass myList = [1,2,3,4] func(*myList) def a(*args): print(args) # x = '123' print(a(*x))
5、函数也是一种数据
函数也是一种数据,那么就可以用变量来保存(函数名也是一个特殊的变量)
应用:将函数作为参数传递(回调函数)
def speak():
print('Lucky老师真的帅啊')
def test(say):
say()
test(speak)
三、函数的例子
1、实现类似pop 函数的功能
def myPop(myList):
x = myList[-1]
del myList[-1]
return x
x = [1,2,3]
print(myPop(x))
2、 实现类似popitem()函数的功能 删除最后一对键值对 并返回
Dict = {'a':'a'}
def a(Dict):
keys = list(Dict.keys())
# print(keys[-1])
myTup = (keys[-1],Dict[keys[-1]])
del Dict[keys[-1]]
return myTup
print(a(Dict))
print(Dict)
3、定义一个函数 实现 字典得键值交换
Dict = {'a':'b','c':'d'}
def Demo(Dict):
newDict = {}
for i in Dict:
# print(i)
newDict[Dict[i]] = i
return newDict
print(Demo(Dict))
4、计算器
def add(x, y):
"""相加 """
return x + y
def subtract(x, y):
"""相减"""
return x - y
def multiply(x, y):
"""相乘"""
return x * y
def divide(x, y):
"""相除"""
return x / y
# 用户输入
print("选择运算:")
print("1、相加")
print("2、相减")
print("3、相乘")
print("4、相除")
choice = input("输入你的选择(1/2/3/4):")
num1 = int(input("输入第一个数字: "))
num2 = int(input("输入第二个数字: "))
# num1,num2 = eval(input('输入'))
if choice == '1':
print(num1, "+", num2, "=", add(num1, num2))
elif choice == '2':
print(num1, "-", num2, "=", subtract(num1, num2))
elif choice == '3':
print(num1, "*", num2, "=", multiply(num1, num2))
elif choice == '4':
print(num1, "/", num2, "=", divide(num1, num2))
else:
print("非法输入")
四、变量的作用域
1、作用域
-
概念
Python 中,程序的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的。
-
作用域的划分
- 局部作用域(L)
- 闭包函数外到函数中(E)
- 全局作用域(G)
- 内建作用域(B)
-
变量查找的规则
L->E->G->B
以 L –> E –> G –>B 的规则查找,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找。不会向低级作用域中查找
-
注意
Python 中只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域,其它的代码块(如 if/elif/else/、try/except、for/while等)是不会引入新的作用域的,也就是说这这些语句内定义的变量,外部也可以访问
x = int(2.9) # 内建作用域 g_count = 0 # 全局作用域 def outer(): o_count = 1 # 闭包函数外的函数中 def inner(): i_count = 2 # 局部作用域
2、作用域体现
在函数外部声明的变量为全局变量 在函数外部声明的变量为局部变量
num = 10 #全局变量
def demo():
num = 2 #局部变量
在函数外部声明的变量 在函数内部可以获取到 在函数内部声明的变量在函数外部不能够获取到
num = 3
def demo():
print(num)
注意:
a = 10
def test():
a = a + 1
print(a)
test()
错误信息为局部作用域引用错误,因为 test 函数中的 a 使用的是局部,未定义,无法修改。
3、global 修改全局变量的值
关键字 global 声明变量函数内外使用同一个
str1 = x
def test():
global str1 #将str1变量 声明为 全局变量 内外使用的都为同一个变量
#否则在函数内部是可以获取函数外的变量 但是修改以后 就会生成一个新的局部变量 和函数外的变量 不是同一个变量
str1 = 2
test()
print(str1)
4、nonlocal 修改嵌套作用域中变量的值
如果函数发生嵌套 那么内部的函数通过 nonlocal 可以获取外层函数的变量
def Test():
num=10 #函数内部都是局部变量
def Testin():
nonlocal num #内层testin,直接操作外层Test,
num=1000 #没有noloacal解释为新建一个变量,否则解释为重新赋值
print("test in",num,id(num))
Testin()
print("test",num,id(num))
Test()
5、值传递与引用传递
不可变类型:类似 c++ 的值传递,如 整数、字符串、元组。如fun(a),传递的只是a的值,没有影响a对象本身。比如在 fun(a) 内部修改 a 的值,只是修改另一个复制的对象,不会影响 a 本身。
可变类型:类似 c++ 的引用传递,如 列表,字典。如 fun(la),则是将 la 真正的传过去,修改后fun外部的la也会受影响
-
值传递:传递不可变数据类型
在函数内部如果修改函数外部的变量(如整形 字符串...) 那么在函数内部相当于是声明了一个新的变量 和函数外部的变量没有关联 (如列表 是可以修改值 但是重新赋值一个新的列表 那么就为一个新的变量)
num = 10 def ChangeInt(): num = 20 print(num) # 20 此刻num为局部变量 一个新的变量 和函数外部的变量没有关联 ChangeInt() print(num) # 10
实例中有 int 对象 2,指向它的变量是 b,在传递给 ChangeInt 函数时,按传值的方式复制了变量 b,a 和 b 都指向了同一个 Int 对象,在 a=10 时,则新生成一个 int 值对象 10,并让 a 指向它
-
引用传递:传递可变数据类型
可变对象在函数里修改了参数,那么在调用这个函数的函数里,原始的参数也被改变了
#!/usr/bin/python3 # 可写函数说明 def changeme( mylist ): "修改传入的列表" mylist.append([1,2,3,4]); print ("函数内取值: ", mylist) return # 调用changeme函数 mylist = [10,20,30]; changeme( mylist ); print ("函数外取值: ", mylist)
传入函数的和在末尾添加新内容的对象用的是同一个引用。故输出结果如下:
函数内取值: [10, 20, 30, [1, 2, 3, 4]] 函数外取值: [10, 20, 30, [1, 2, 3, 4]]
6、闭包
概念:又称闭包函数或者闭合函数,其实和前面讲的嵌套函数类似,不同之处在于,闭包中外部函数返回的不是一个具体的值,而是一个函数
优点:避免污染全局环境,这样就可以在函数体外使用函数体中定义的变量
缺点:数据会长期驻留在内存中,造成内存极大的浪费
注意:尽量避免使用闭包
-
示例
def func1(): b = 2 def func2(): return b return func2 f2 = func1() print(f2())
-
参数延迟绑定(闭包的坑)
def func(): arr = [] for i in range(3): def f(): print("-----------", i) arr.append(f) return arr li = func() li[0]() li[1]() li[2]()
五、匿名函数
关键字 lambda
不在使用def语句这样标准的形式定义函数,而是使用lambda来创建匿名函数
格式:lambda [arg1[, arg2[, args,……]]]: exception
作用:作为参数传递,实现回调,简化代码
特点:
lambda只是一个表达式,函数体比def简单的多
lamdba主体是一个的表达,而不是代码块,仅能在lamdba表达式中封装有限的逻辑
lamdba函数拥有自己的命名空间,且不能访问自有参数列表之外的或全局命名空间里的参数
虽然lamdba函数体看起来只有一行,却不同于C和C++的内联函数,后者的目的是调用小函数时不占用栈内存而增加效率
lambda 只是一个表达式,函数体比 def 简单很多
lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去
lambda 函数拥有自己的命名空间,且不能访问自有参数列表之外或全局命名空间里的参数。
实例
第一种
#定义匿名函数 返回a+b的值
func = lambda a,b:a+b
#调用匿名函数
func(1,2)
第二种 定义并调用
(lambda a,b:a+b)(100,200)
第三种 有参数有默认值
g= lambda x=0,y=0 : x**2+y**2
g(2,3)
第四种 没有参数的
a = 10
func = lambda:a
print(func())
第五种 嵌套
func = lambda:(lambda:print('lucky'))()
六、递归函数
概述:
递归函数就是自己调用自己的一个过程 当条件不满足则停止递归调用
1、递归阶乘
def fact(n):
if n==1:
return 1
return n * fact(n - 1)
如果我们计算fact(5)
,可以根据函数定义看到计算过程如下:
===> fact(5)
===> 5 * fact(4)
===> 5 * (4 * fact(3))
===> 5 * (4 * (3 * fact(2)))
===> 5 * (4 * (3 * (2 * fact(1))))
===> 5 * (4 * (3 * (2 * 1)))
===> 5 * (4 * (3 * 2))
===> 5 * (4 * 6)
===> 5 * 24
===> 120
递归函数的优点是定义简单,逻辑清晰。理论上,所有的递归函数都可以写成循环的方式,但循环的逻辑不如递归清晰。
使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。
试fact(1000)
:
2、递归求和
#输入一个数(大于等于1),求1+2+3+……+n的和
#普通写法
def sum1(n):
sum = 0
for x in range(1, n + 1):
sum += x
return sum
#递归写法
def sum2(n):
if n == 1:
return 1
else:
return n + sum2(n - 1)
res = sum2(5)
3、 递归进行值的输出
def demo(n):
print(n)
if n>1:
demo(n-1)
print(n)
4、递归的分解
def a(x):
print(x)
b(x-1)
print(',',x)
def b(x):
print(x)
c(x-1)
print(x)
def c(x):
print(x)
print(x)
a(3)
通过递归实现斐波那契数列
def recur_fibo(n):
"""递归函数
输出斐波那契数列"""
if n <= 1:
return n
else:
return(recur_fibo(n-1) + recur_fibo(n-2))
# 获取用户输入
nterms = int(input("您要输出几项? "))
# 检查输入的数字是否正确
if nterms <= 0:
print("输入正数")
else:
print("斐波那契数列:")
for i in range(nterms):
print(recur_fibo(i))
七、高阶函数
1、map&reduce
-
概述
Python内建了map()和reduce()函数
-
map()函数
原型:map(fn, lsd)
参数:
fn:是一个函数 lsd:集合
功能:将传输的函数fn依次作用到lsd集合中的每个元素,并把结果作为一个Iterator返回
myList = [1,2,3,4,5] def my_func(arg): return arg**2 res = map(my_func,myList) print(list(res)) print(tuple(res)) print(set(res)) # 使用lambda表达式来实现 res = map(lambda x:x**2,myList) print(list(res)) # 使用内置数据类型转换函数实现 res = map(int,['1','2','3','4','5']) res = map(bool,[0,'1','2','3','','4',(),{},'5']) print(list(res))
-
reduce()函数
原型:reduce(fn, lsd)
参数:
fn:一个函数
lsd:集合功能:传入的fn函数作用在lsd集合中,这个fn函数必须接收两个参数,reduce把结果继续和序列中的下一个元素做累积运算
from functools import reduce # 进行值的累加 def add(x,y): print(x,y) return x+y """ 1 2 3 3 6 4 10 5 15 """ my_list = [1,2,3,4,5] # res = reduce(add,my_list) # 使用lambda表达式实现 res = reduce(lambda x,y:x+y,my_list) print(res)
-
reduce&map组合使用案例
from functools import reduce #将字符串转成对应字面量数字 def str2int(str): def fc(x, y): # print(x,'*',10,"+",y) #打印查看运行结果 return x * 10 + y def fs(chr): return int(chr) return reduce(fc, map(fs, list(str))) a = str2int("12345") print(a) print(type(a)) """ 12345 1+2 1*10+2 3+3 12*10+3 6+4 123*10+4 10+5 1234*10+5 12345 """
使用lambda表达式实现
# 将字符串的 '12345' 变成整形的12345 print(reduce(lambda x,y:x*10+y,list(map(lambda x:int(x),list('12345')))))
2、filter
原型:filter(fn, lsd)
参数:
fn:函数
lsd:集合
功能:用于过滤列表,把传入的fn函数依次作用在lsd集合中的每个元素上,然后根据返回True还是False决定是否保留该元素
li = [1,2,3,4,5,6,7,8,9,10]
# def func1(arr):
# for x in arr:
# if x % 2 == 0:
# arr.remove(x)
# func1(li)
def func2(num):
if num % 2 == 0:
return False
return True
res = filter(func2, li)
print(res)
print(list(res))
print(li)
删除列表中是空字符串的元素
li2 = ["a", "", "", "c", " ", " bsg "]
def f(item):
return item and item.strip()
res = filter(f, li2)
print(list(res))
3、sorted
-
排序算法
冒泡排序、快速排序、选择排序,计数器排序等
-
效率
在数据量小时冒泡和快速没有什么区别,但是当处理大量数据时快速排序的效率明显高于冒泡排序
-
冒泡算法原理
1 2 3 4 5 -> 5 4 3 2 1 1 2 3 4 5 -> 2 1 3 4 5 -> 2 3 1 4 5 -> 2 3 4 1 5 -> 2 3 4 5 1 0 1 1 2 2 3 3 4 2 3 4 5 1 -> 3 2 4 5 1 -> 3 4 2 5 1 -> 3 4 5 2 1 3 4 5 2 1 -> 4 3 5 2 1 -> 4 5 3 2 1 4 5 3 2 1 -> 5 4 3 2 1
代码实现
li = [1,2,3,4,5] for i in range(len(li) - 1): for j in range(len(li) - i - 1): if li[j] < li[j+1]: temp = li[j] li[j] = li[j+1] li[j+1] = temp print(li)
-
sorted()函数
原型:sorted(lsd, key=func)
参数:
lsd:集合
key:规定排序的规则功能:将led中的每个元素作用在key函数上,用函数的结果的大小来排序
优点:可以自定义排序规则def func(x): return ord(x[2]) li2 = ["awaefweg","sgeweg3","sgwrtwe","wr2","ergstrhrt"] li3 = sorted(li2, key=len, reverse=True) li3 = sorted(li2, key=func, reverse=True) print(li2) print(li3) #按绝对值大小排序 list3 = [4,-7,2,6,-3] #key接受函数来实现自定义排序规则 list4 = sorted(list3, key=abs)
八、装饰器
1、概念
装饰器: 装饰器本质上是一个Python函数,其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值(return)也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。
在代码运行期间动态的增加功能 称之为装饰器
2、 函数回顾
-
函数普通调用
def say(): return "hi lucky" print(say()) # 我们甚至可以将一个函数赋值给一个变量,比如 speak = say # 我们这里没有在使用小括号,因为我们并不是在调用hi函数 # 而是在将它放在say变量里头。我们尝试运行下这个 print(speak())
-
在函数中定义函数
def demo(name="lucky"): print("now you are inside the hi() function") def inner1(): return "now you are in the inner1() function" def inner2(): return "now you are in the inner2() function" print(inner1()) print(inner2()) print("now you are back in the demo() function") demo()
上面展示了无论何时你调用hi(), greet()和welcome()将会同时被调用。
然后greet()和welcome()函数在hi()函数之外是不能访问的,比如:
-
函数内调用外部函数
def demo(x): print('我是demo函数') x() def func(): print('我是func函数') #在其它语言里 称为 回调函数 也就是传进去的参数是一个函数名 demo(func)
3、无参装饰器
-
在lucky is a good man 上下分别实现输出一句话
def wrapper(fun): def inner(): print('-----------') a = fun() print('-----------') return a return b def say(): print('lucky is a good man') return 1 inner = wrapper(say) print(inner())
使用@符号装饰
python2.4开始支持使用@符号将装饰器应用到函数上,只需要在函数定义时加上"@装饰器名称"即可完成装饰操作
def wrapper(fun): def inner(): print('-----------') a = fun() print('-----------') return fun return inner @wrapper def say(): print('lucky is a good man') return 1 say()
-
日志打印
def foo(): print('i am foo') def foo(): print('i am foo') logging.info("foo is running")
如果其它函数也有类似的需求,怎么做?再写一个logging在bar函数里?这样就造成大量雷同的代码,为了减少重复写代码,我们可以这样做,重新定义一个函数:专门处理日志 ,日志处理完之后再执行真正的业务代
def use_logging(func): logging.warning("%s is running" % func.__name__) func() def bar(): print('i am bar') use_logging(bar)
逻 辑上不难理解, 但是这样的话,我们每次都要将一个函数作为参数传递给use_logging函数。而且这种方式已经破坏了原有的代码逻辑结构,之前执行业务逻辑时,执行 运行bar(),但是现在不得不改成use_logging(bar)。那么有没有更好的方式的呢?当然有,答案就是装饰器。
import logging def use_logging(func): def inner(): func() logging.warning("%s is running" % func.__name__) return inner @use_logging def bar(): print('i am bar') @use_logging def bar2(): print('i am bar2') # use_logging(bar) # use_logging(bar2) bar() bar2()
-
统计函数执行次数案例
def wrapper(f): count = 1 def inner(*args, **kwargs): nonlocal count print("第%d次执行"%(count)) res = f(*args, **kwargs) count += 1 return res return inner @wrapper def say(): print("lucky is a good man") say() say() say()
-
统计函数运行时间案例
import time def wrapper(f): def inner(*args, **kwargs): t1 = time.time() #获取当前时间的时间戳 res = f() t2 = time.time() print("耗时%.2f秒"%(t2-t1)) return res return inner @wrapper def say(): print("lucky is a good man") # 休息2秒钟,可以是浮点数 time.sleep(2) print("lucky is a nice man") say()
4、带参数的装饰器
如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本:
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
这个3层嵌套的decorator用法如下:
@log('execute')
def now():
print('2015-3-25')
执行结果如下:
>>> now()
execute now():
2015-3-25
和两层嵌套的decorator相比,3层嵌套的效果是这样的:
>>> now = log('execute')(now)
我们来剖析上面的语句,首先执行log('execute')
,返回的是decorator
函数,再调用返回的函数,参数是now
函数,返回值最终是wrapper
函数。
以上两种decorator的定义都没有问题,但还差最后一步。因为我们讲了函数也是对象,它有__name__
等属性,但你去看经过decorator装饰之后的函数,它们的__name__
已经从原来的'now'
变成了'wrapper'
:
>>> now.__name__
'wrapper'
因为返回的那个wrapper()
函数名字就是'wrapper'
,所以,需要把原始函数的__name__
等属性复制到wrapper()
函数中,否则,有些依赖函数签名的代码执行就会出错。
不需要编写wrapper.__name__ = func.__name__
这样的代码,Python内置的functools.wraps
就是干这个事的,所以,一个完整的decorator的写法如下:
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('call %s():' % func.__name__)
return func(*args, **kw)
return wrapper
或者针对带参数的decorator:
import functools
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print('%s %s():' % (text, func.__name__))
return func(*args, **kw)
return wrapper
return decorator
import functools
是导入functools
模块。模块的概念稍候讲解。现在,只需记住在定义wrapper()
的前面加上@functools.wraps(func)
即可。
九、偏函数
1、int()
int()
函数可以把字符串转换为整数,当仅传入字符串时,int()
函数默认按十进制转换:
>>> int('12345')
12345
但int()
函数还提供额外的base
参数,默认值为10
。如果传入base
参数,就可以做N进制的转换:
>>> int('12345', base=8)
5349
>>> int('12345', 16)
74565
2、思考
大量进行二进制转换
假设要转换大量的二进制字符串,每次都传入int(x, base=2)
非常麻烦,于是,我们想到,可以定义一个int2()
的函数,默认把base=2
传进去:
def int2(x, base=2):
return int(x, base)
这样,我们转换二进制就非常方便了:
>>> int2('1000000')
64
>>> int2('1010101')
85
3、偏函数实现
functools.partial
就是帮助我们创建一个偏函数的,不需要我们自己定义int2()
,可以直接使用下面的代码创建一个新的函数int2
:
>>> import functools
>>> int2 = functools.partial(int, base=2) [ˈpɑ:rʃl
>>> int2('1000000')
64
>>> int2('1010101')
85
所以,简单总结functools.partial
的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。
注意到上面的新的int2
函数,仅仅是把base
参数重新设定默认值为2
,但也可以在函数调用时传入其他值:
>>> int2('1000000', base=10)
1000000
最后,创建偏函数时,实际上可以接收函数对象、*args
和**kw
这3个参数,当传入:
int2 = functools.partial(int, base=2)
实际上固定了int()函数的关键字参数base
,也就是:
int2('10010')
相当于:
kw = { 'base': 2 }
int('10010', **kw)
当传入:
max2 = functools.partial(max, 10)
实际上会把10
作为*args
的一部分自动加到左边,也就是:
max2(5, 6, 7)
相当于:
args = (10, 5, 6, 7)
max(*args)
结果为10
。
小结
当函数的参数个数太多,需要简化时,使用functools.partial
可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。