Python 函数参数之不定长参数(*args/**kwargs)、匿名函数 Lambda详解


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 函数的好处和局限都很明显,到底要不要使用和什么使用需要自我实践了,只要在合理的时候使用合理的操作才是最合理的。

以上就是匿名函数的一些常用操作,当然还有更多的高级用法,有兴趣的同学可以自行查阅资料。如果有疑问也可以给我留言

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342