Python 调用函数时可使用的正式参数类型:
必需参数 (位置参数)、关键字参数 (key=value)、默认参数 (key=default)、不定长参数(可变参数)、强制位置参数(组合传参)
Tips:有兴趣的还可以了解一下什么是形参?什么是实参?
不定长参数 *args **kwargs
Python 在定义函数的过程中,当你可能需要一个函数能处理比当初声明时更多的参数。那么就会用到 *arg、**kwargs 称之为不定长参数,声明时不会命名;
*args 具体语法操作如下:
def func(a, *args):
print(a)
print(args)func(1, 2)
结果输出:
1
(2,)注意:加了星号 * 的参数会以元组(tuple)的形式导入,存放所有未命名的变量参数。
如果在函数调用时没有指定参数,它就是一个空元组:
def func(a, *args):
print(a)
print(args)func(10)
func(1, 2)结果输出:
结果:
10
()
结果:
1
(2,)
从上面的示例来看,如果传入的参数超过了位置参数,后面的参数都会以元组的来接收,那么如果我们直接传入一个元组参数行不行?
t = (1, 2, 3)
def func(a, *args):
print('结果:')
print(a)
print(args)func(1, 2, t )
结果:
1
(2, (1, 2, 3))
从以上示例来看目前是可以的,但是如果我们需要对参数进行计算或者其他操作的时候呢?下面再来看一个列子:
t = (1, 2, 3)
def avg(a, *args):
return (a + sum(args)) / (len(args) + 1)print(avg(20, t))
结果:
TypeError: unsupported operand type(s) for +: 'int' and 'tuple'
结果就报错了,原因是因为你直接传入的是一个元组,int类型和元组是不能直接进行计算的,那么怎么解决这个问题?其实很简单看下面的实例:
t = (1, 2, 3)
def avg(a, *args):
return (a + sum(args)) / (len(args) + 1)print(avg(20, *t))
结果:
6.5
其实很简单,只需要在传 t 元组的时候加上一个 * 就可以了,称之为解包,就是把元组打散,分开传入.
*kwargs 具体语法操作如下:
d = {'name': 'amy', 'age': 18}
t = (1, 2, 3)def func(a, *args, **kwargs):
print('结果:')
print(f'位置参数:{a}')
print(f'不定长元组参数:{args}')
print(f'不定长键值对参数:{kwargs}')func(1, t, d)
结果:
位置参数:1
不定长元组参数:((1, 2, 3), {'name': 'amy', 'age': 18})
不定长键值对参数:{}
咦,发现实际结果并不是我们想要的,键值对的参数跑到元组里面去了,那下面看一下正确的传入方式:
t = (1, 2, 3)
def func(a, *args, **kwargs):
print('结果:')
print(f'位置参数:{a}')
print(f'不定长元组参数:{args}')
print(f'不定长键值对参数:{kwargs}')func(1, t, name='angst', age=18)
结果:
位置参数:1
不定长元组参数:((1, 2, 3),)
不定长键值对参数:{'name': 'angst', 'age': 18}
正确的参数传入方式就是通过关键字参数的方式进行传入,这样就达到了我们想要的目的,但是,如果我就是想传入一个字典类型的参数进去怎么办?
d = {'name': 'amy', 'age': 18}
t = (1, 2, 3)def func(a, *args, **kwargs):
print('结果:')
print(f'位置参数:{a}')
print(f'不定长元组参数:{args}')
print(f'不定长键值对参数:{kwargs}')func(1, t, **d)
结果:
位置参数:1
不定长元组参数:((1, 2, 3),)
不定长键值对参数:{'name': 'amy', 'age': 18}
其实也是可以的,方法跟元组的类似,参数在传入的时候加上 ** 就行了。那么最后一个问题?怎么获取对应不定长参数的值呢?
d = {'name': 'amy', 'age': 18}
t = (1, 2, 3)def func(a, *args, **kwargs):
print('结果:')
print(f'位置参数:{a}')
print(f'不定长元组第一个参数:{args[0][0]}')
print(f'不定长键值对名称参数:{kwargs.get("name")}')func(1, t, **d)
结果:
位置参数:1
不定长元组第一个参数:1
不定长键值对名称参数:amy
从上面的列子可以看出,其实获取的并没有什么不同,元组通过下标获取,字典通过Key获取。
匿名函数 Lambda
python 使用 lambda 来创建匿名函数。所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。简单来说就是不用给函数定义名称;
> lambda 只是一个表达式,函数体比 def 简单很多。
>lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。
>lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。
>虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率
以上是 菜鸟教程 的解释,下面来看具体的示例:
def hello(name):
print(f'hello {name}')hello('Amy')
angst = lambda name: print(f'hello {name}')
print(angst)
结果输出:
hello Amy
<function <lambda> at 0x0000026EE6F34048>
一个是正常的 hello() 函数定义和调用,又定义了一个lambda 函数赋值给了变量 angst,两个函数的实际功能其实是一直的,但是从上面输出的结果来看 angst 出输出的是一个函数对象在内存的地址;其实并没有调用,只是打印了angst 函数变量而已,正确的调用方式其实跟正常的函数一样,在变量后面加上一个()
def hello(name):
print(f'hello {name}')hello('Amy')
angst = lambda name: print(f'hello {name}')
angst('angst')
结果输出:
hello Amy
hello angst
从上面可以看出,其实匿名函数从本质上来说并没有不同,只是使用的场景稍微有些特许而已,由于lambda 函数没有定义名称,所有我们在定义的时候通过会赋值一个变量去引用,之前的变量一般都是一些参数类型,但是通过lambda的形式将函数赋值给变量,通过变量调用该函数时需要带上(),如果有参数就在()传参即可。lambda 的函数一般是用于比较简单的函数表达。lambda 函数还有一个特性就是自带 return 关键字,可以自动把结果返回给函数本身。
lambda:None;函数没有输入参数,输出是 None
lambda x, y: x+y;函数输入是x和y,输出是它们的和
lambda *args: sum(args); 输入是任意个数的参数,输出是它们的和(隐性要求是输入参数必须能够进行加法运算)
lambda **kwargs: kwagrs.get(key);输入是任意键值对参数,输出是key 对应的value
根据这个lambda函数应用场景的不同,可以将lambda函数的用法有以下几种 参考博客:
1>.将lambda函数赋值给一个变量,通过这个变量间接调用该lambda函数,调用时记得带上()
2>.将lambda函数赋值给其他函数,从而将其他函数用该 lambda 函数进行替换.
例:time.sleep=lambda x:None,在后续代码中调用time库的sleep函数将不会执行原有的功能。执行time.sleep(3)时,不会休眠3秒而是None
3>.将lambda函数作为其他函数的返回值,返回给调用者
例如:return lambda x, y: x+y 返回一个加法函数。lambda函数实际上是定义在某个函数内部的函数,称之为嵌套函数,或者内部函数。对应的,将包含嵌套函数的函数称之为外部函数。内部函数能够访问外部函数的局部变量,这个特性是闭包(Closure)编程的基础,在后面的高级教程装饰器中会用到,这里就不描述具体的用法了,后续可以关注我的博客,有专门对阵装饰器的用法讲解。
4>.将lambda函数作为参数传递给其他函数
例:部分Python内置函数接收函数作为参数。map函数:lambda函数用于指定对列表中每一个元素的共同操作。如:map(lambda x: x+1, [1, 2,3])将列表[1, 2, 3]中的元素分别加1,其结果[2, 3, 4]
lambda的使用一直存在一些争议,lambda 函数的好处和局限都很明显,到底要不要使用和什么使用需要自我实践了,只要在合理的时候使用合理的操作才是最合理的。
以上就是匿名函数的一些常用操作,当然还有更多的高级用法,有兴趣的同学可以自行查阅资料。如果有疑问也可以给我留言。