python-自定义函数详解(匿名函数、偏函数、装饰器)

python-自定义函数

函数定义

def关键字定义一个函数,def后必须后跟函数名称和带括号的形式参数列表。构成函数体的语句从下一行开始,并且必须缩进。

'''定义一个函数:1+2+3...+num'''
def cumsum(num):
	i = 0
	for each in range(1,num + 1):
		i += each
	return i
	
'''或者也可以用递归写'''
def cumsum(num):
    if num > 1:
        return num + cumsum(num - 1)
    else:
        return num
        
cumsum(10)		#调用函数,传入参数10
#out: 55

设置默认参数值

def cumsum(end=10, start=1):
	i = 0
	for each in range(start,end + 1):
		i += each
	return i
cumsum()
#out: 55	函数参数调用默认值

'''我们也可以参数传入其他值,如果传入的参数没有指定参数名称,需要按照函数定义参数的顺序传入,这里end=5,start=2'''
cumsum(5,2)
#out: 14	

'''如果指定参数名称赋值,可以不按照函数定义参数顺序传入,这里使用参数名称赋值,把start参数放在了开头。'''
cumsum(start=2, end=5)
#out: 14		

可变参数

*args,加“*”号为若干个参数,返回元组,*args放在普通参数后。

def cumsum(num,*args):
	for each in args:	#args是一个元组
		num += each
	return num

cumsum(1,5,6,7)
#out:19	num后面参数5,6,7以元组args传入函数内。

关键字参数

虽然一个参数可以接受多个实参,但是这些实参都被捆绑为元组了,而且无法将具体的实参指定给具体的形参。关键字参数,可以把带参数名的参数值组装到一个字典中,键就是具体的实参名,值就是传入的参数值。**kwargs:注意关键字参数定义时,放在最后面,如果存在可变参数,就放在可变参数定义后面。

def update_dic(name,age,**kwargs):
    person_info = {}
    person_info['name'] = name
    person_info['age'] = age
    person_info.update(kwargs)
    return person_info
    
update_dic('张三',18,weight='70kg', height='178cm')
#out: {'name': '张三', 'age': 18, 'weight': '70kg', 'height': '178cm'}	weight='70kg', height='178cm'以字典kwargs传入函数内

解包参数列表

函数定义时,*args和**kwargs会将参数以元组和字段返回到函数内调用,相反地,在函数调用时,传参前加上*和**表示解包,将元组列表拆解为元素,下面来做个示例:

def cumsum(num,*args):
	for each in args:	#args是一个元组
		num += each
	return num
	
cumsum(1,2,3,4) 
#out: 10	正常情况下,我们在num参数后,逐个输出arg参数

'''我们也传入*列表代表args'''
ls = [2,3,4]
cumsum(1,*ls)
#out: 10	传入*列表表示解包列表为单个元素,*ls就相当于2,3,4。

'''关键字参数也是一样的,我们暂且还是借用上面的函数'''
def update_dic(name,age,**kwargs):
    person_info = {}
    person_info['name'] = name
    person_info['age'] = age
    person_info.update(kwargs)
    return person_info
    
dic = {'weight': '70kg', 'height': '178cm'}
'''我们直接传入字典,这里**dic跟直接传入weight='70kg', height='178cm'是一样的。'''
update_dic('张三',18,**dic)
#out: out: {'name': '张三', 'age': 18, 'weight': '70kg', 'height': '178cm'}		

我们或可以这么理解,对于函数定义时使用了可变参数:*args和关键字参数:**kwargs,函数调用时,对应的参数是元素,以元组,字典返回到函数内部调用。如果调用函数参数使用 *列表或元组 | **字典会将列表或元组|字典解包成元素传参,到函数内部时,依旧是元组|字典,只是传参的形式不一样罢了。


匿名函数:lambda

使用lambda关键字定义函数,lambda var :exp(var)

fun1 = lambda x : x + 1

def fun2(x):
	return x + 1
	
fun1(1)
#out: 2

fun2(1)
#out: 2

'''这里的fun1函数和fun2函数是一样的,如果函数设计比较简单,我们完全可以使用lambda方式定义函数,书写简洁。'''

关于局部变量与全局变量**

函数内部的变量如果没有声明,只在函数内部生效,全局变量需要使用global关键字声明,来个简单的栗子:

a = 1

def about_global()
	a = 10
	return a

'''a=10,在函数内部有效,正常返回'''
about_global()
#out: 10

'''函数内部的赋值,a=10,函数外无效,仍然返回1'''	
print(a)
#out: 1		

def about_global()
'''声明a为全局变量'''
	global a	
	a = 10
	return a

'''a=10,函数内部始终有效'''	
about_global()
#out: 10

'''函数内部定义a为全局变量,函数外,a变量也随着函数内修改而修改,返回10'''	
print(a)
#out: 10		

偏函数的使用

functools.partial可以帮助我们创建一个偏函数,它把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单。

import functools

def cumsum(end=10, start=1):
		i = 0
		for each in range(start,end + 1):
			i += each
		return i

cumsum()
#out: 55

'''创建一个cumsum偏函数'''
cumsum_patrial  = functools.partial(cumsum,end=5,start=1)

cumsum_partial()
#out: 15	函数的默认参数修改了。

其他

函数参数里可以传入类,也可以传入函数方法名称

class TT:
   @classmethod		# 类方法,无需实例,类可以直接调用
   def pt(cls):
       print("执行了类TT的方法:pt")

def test(cls):
    cls.pt()

test(TT)

#out: 执行了类TT的方法:pt

装饰器

在不改变原函数代码调用方式的前提下,为原函数添加一些扩展功能,比如下面的计算函数耗时(通过outer传参控制装饰函数是否生效)。
这里我们定义了一个outer装饰器,函数定义上方带有“@装饰器名”,在实际调用该函数时,实际调用的是装饰器函数内最终的return函数:

import time

def outer(flag):
    def timer(fun):
        def inner(*args,**kwargs):		# 不定长参数
            if flag:
                start = time.time()
            fun(*args,**kwargs)			# 这里的args是元组,kwargs是字段,前面加‘*’号解包
            if flag:
                print('函数名:"{}";运行耗时:{:.2f}'.format(fun.__name__,time.time()-start),fun.__doc__)
        return inner
    return timer

'''装饰函数传入‘False’参数,装饰效果不生效'''
@outer(False)
def fun1(num):
    '''
    这里是函数说明文档:
    param num:整数
    return:返回0+1+2+3+...+(num-1)的累加和
    '''
    i = 0
    for each in range(num):
        i += each
    print(i)

fun1(10000000)

'''这里fun1函数我们去掉@outer(False)装饰,下面的方法是等价于函数fun1上添加@outer(True)装饰'''
outer(True)(fun1)(10000000)
# outer(True)返回timer函数对象引用;
# outer(True)(fun1)返回inner函数对象引用;
# outer(True)(fun1)(10000000):inner函数传参:10000000,执行inner函数

'''
使用装饰器@outer(False)调用fun1(10000000)
out:49999995000000

不使用装饰器执行outer(True)(fun1)(10000000)
out:49999995000000
out:函数:"fun1";运行耗时:0.54 
   out:这里是函数说明文档:
   out:param num:整数
   out:return:返回0+1+2+3+...+(num-1)的累加和
'''

'''不带参数的装饰器'''
def timer(func):
	def inner(*args,**kwargs):
		start_time = time.time()
		func(*args,**kwargs)
		end_time = time.time()
		print('{}函数已执行完毕,耗时{:.2f}s'.format(func.__name__,end_time-start_time)

@timer
def func1():
	pass

函数传值还是传引用的问题

传值:函数内参数变量修改,被传外部变量不会修改;
传引用:通常是可变对象,函数内参数变量修改,被传外部变量会修改;函数内变量重新赋值指向另一块内存,不再影响外部变量;;
函数参数在传递的过程中将整个对象传入,对可变对象的修改在函数外部以及内部都可见,调用者和被调用者之间共享这个对象,而对于不可变对象,对象并不能真正被修改,修改往往是通过生成一个新对象然后赋值来实现的。

ls = [1]
def ls_append(var_ls):
	var_ls.append(2)
	print('var_ls','id_var_ls',id(var_ls))
	var_ls = [2,3,4]    # val_ls指向了另一个内存地址,id发生变化
	print(var_ls,'id_new_var_ls',id(var_ls))
ls_append(ls)
print('ls',ls,'id_ls',id(ls))
'''out
我们发现,函数的引用valA_ls和外部引用ls指向同一个对象:id是一样的,列表是可变对象
var_ls id_var_ls 1868243761736
[2, 3, 4] id_new_var_ls 1868243965192
ls [1, 2] id_ls 1868243761736
'''
s = 'a'
def str_add(val_s):
	print(val_s,'id_val_s:',id(val_s))
	val_s = 'b'
	print(val_s,'id_val_s_b:',id(val_s))
str_add(s)
print(s,'id_s:',id(s))
'''out
val_s未被赋值前,id和局外变量s是一样的,被赋值,id不一样
a id_val_s: 1867309509808
b id_val_s_b: 1867309494128
a id_s: 1867309509808
'''

默认参数的问题

def在Python中是一个可执行的语句,解释器执行def的时候,默认参数会被计算,存在函数的func._defaults__属性中。由于Python中函数参数传递的是对象,可变对象在调用者和被调用者之间共享;

def add_one(num=[]):
	num.append(1)
	print(num)
add_one()
add_one()
'''out
[1]
[1, 1]
'''
print(add_one.__defaults__)

如果不想让默认参数所指向的对象在所有的函数调用中被共享,而是在函数调用的过程中动态生成,可以在定义的时候使用None对象作为占位符


变长参数使用问题

如果一个函数定义了普通变数、默认参数、可变长参数,传参会变得很灵活,这种灵活也往往使得可读性比价差,能使用可迭代对象参数,尽量不要使用args;

def func(a,b,va=1,vb=2,*args,**kwargs):
	print(a,b,va,vb,args,kwargs)

'''以下几种传参都是可以被允许的'''
func(1,2,3)
func(1,2,3,4)
func(1,2,kw1=5)
func(1,2,23,4,5,6,kw2=100)	
'''out
1 2 3 2 () {}
1 2 3 4 () {}
1 2 1 2 () {'kw1': 5}
1 2 23 4 (5, 6) {'kw2': 100}
'''

函数的参数传递:除关键字参数kwargs(要定义写在最后面),其他从左往右,先传给普通参数,再传给默认参数,其他再如果还有传参传给args参数,最后再是关键字参数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值