一文弄懂python中的函数(自定义函数,匿名函数,高阶函数与解析式)

什么是函数,如何在 Python 程序中定义函数呢?

说白了,函数就是为了实现某一功能的代码段,只要写好以后,就可以重复利用。

python中的自定义函数

Python 的函数具有非常灵活多样的参数形态,既可以实现简单的调用,又可以传入非常复杂的参数。从简到繁的参数形态如下:

位置参数 (positional argument)
默认参数 (default argument)
可变参数 (variable argument)
关键字参数 (keyword argument)
命名关键字参数 (name keyword argument)
参数组合
下面的例子中message作为最简单的位置参数:

def my_func(message):
    print('Got a message: {}'.format(message))

#调用函数my_func() my_func('Hello World')
#输出 Got a message: Hello World

在这里插入图片描述
如上图
def - 定义正规函数要写 def 关键词。
function_name - 函数名,起名最好有意义。
arg1 - 位置参数 ,这些参数在调用函数 (call function) 时位置要固定。
arg2 = v - 默认参数 = 默认值,调用函数的时候,默认参数已经有值,就不用再传值了。
:- 冒号,在第一行最后要加个冒号。
“”“docstring”"" - 函数说明,给使用函数这介绍该它是做什么的。
statement - 函数内容。
默认函数一定要放在位置参数后面,不然程序会报错。
「默认参数」可以是多个。

def student( id, shuxue=90, yinyu=95 ):
    print( 'id:', id )
    print( 'shuxue chenji:', shuxue )
    print( 'yinyu chenji:', yinyu)
student('xiaoming')
#输出结果
    id:xiaoming
    shuxue chenji: 90
    yinyu chenji: 95

你可以把默认参数像「位置参数」那样对待,给它设定任何值

def student( id, shuxue=90, yinyu=95 ):
    print( 'id:', id )
    print( 'shuxue chenji:', shuxue )
    print( 'yinyu chenji:', yinyu)
student('xiaoming',100,100)
#输出结果
id: xiaoming
shuxue chenji: 100
yinyu chenji: 100

Python 自定义函数中,还可以定义「可变参数」。顾名思义,可变参数就是传入的参数个数是可变的,可以是 0, 1, 2 到任意个。
在这里插入图片描述
*args - 可变参数,可以是从零个到任意个,自动组装成元组

def student( id, shuxue=90, yinyu=95 , *args ):
    PV = 0
    for n in args:
        PV = PV + n

    print( 'id:', id )
    print( 'shuxue chenji:', shuxue )
    print( 'yinyu chenji:', yinyu)
    print( 'zong chenji:', PV*shuxue )
student('xiaoming',100,100,1,2,3)
#输出结果:
id: xiaoming
shuxue chenji: 100
yinyu chenji: 100
zong chenji: 600

在这里插入图片描述
**kw - 关键字参数,可以是从零个到任意个,自动组装成字典
可变参数允许传入零个到任意个参数,它们在函数调用时自动组装为一个元组 (tuple)
关键字参数允许传入零个到任意个参数,它们在函数内部自动组装为一个字典 (dict)

def student( id, shuxue=90, yinyu=95 , *args,**kw ):
    PV = 0
    for n in args:
        PV = PV + n

    print( 'id:', id )
    print( 'shuxue chenji:', shuxue )
    print( 'yinyu chenji:', yinyu)
    print( 'zong chenji:', PV*shuxue )
    print( 'keyword:', kw)
student('xiaoming',100,100,1,2,3,courtry='china)
#输出结果:
id: xiaoming
shuxue chenji: 100
yinyu chenji: 100
zong chenji: 600
keyword: {'courtry': 'china'}

除了直接传入多个参数之外,还可以将所有参数先组装成字典shuzi,用以「**shuzi」的形式传入函数 (shuzi 是个字典,前面加个通配符 ** 是拆散字典,把字典的键值对传入函数中)

def student( id, shuxue=90, yinyu=95 , *args,**kw ):
    PV = 0
    for n in args:
        PV = PV + n
    
        print( 'id:', id )
        print( 'shuxue chenji:', shuxue )
        print( 'yinyu chenji:', yinyu)
        print( 'zong chenji:', PV*shuxue )
        print( 'keyword:', kw)
kemu = (1, 2, 3, 4, 5)
shuzi = {'dc':'act/365', 'bdc':'following'}
student( 'xiaoming', 100, 100, *kemu, **shuzi )
#运行结果
id: xiaoming
shuxue chenji: 100
yinyu chenji: 100
zong chenji: 1500
keyword: {'dc': 'act/365', 'bdc': 'following'}

在这里插入图片描述
*, kemu - 命名关键字参数,用户想要输入的关键字参数,定义方式是在nkw 前面加个分隔符 *。使用命名关键字参数时,要特别注意不能缺少参数名。

def student( id, shuxue=90, yinyu=95 , *,args,**kw ):
        print( 'id:', id )
        print( 'shuxue chenji:', shuxue )
        print( 'yinyu chenji:', yinyu)
        print( 'zong chenji:', args)
        print( 'keyword:', kw)
shuzi = {'dc':'act/365', 'bdc':'following'}
student( 'xiaoming', 100, 100, *,args=quanke, **shuzi )
    #输出结果
id: xiaoming
shuxue chenji: 100
yinyu chenji: 100
zong chenji: quanke
keyword: {'dc': 'act/365', 'bdc': 'following'}

参数组合

在 Python 中定义函数,可以用位置参数、默认参数、可变参数、命名关键字参数和关键字参数,这 5 种参数中的 4 个都可以一起使用,但是注意,参数定义的顺序必须是:
位置参数、默认参数、可变参数和关键字参数。
位置参数、默认参数、命名关键字参数和关键字参数。

args 是可变参数,args 接收的是一个 tuple
kw 是关键字参数
*,kw 接收的是一个 dict
命名关键字参数是为了限制调用者可以传入的参数名,同时可以提供默认值。定义命名**关键字参数不要忘了写分隔符 ***,否则定义的是位置参数。

python中的匿名函数

除了常规的自定义函数,你应该也会在代码中见到一些“非常规”函数,它们往往很简短,就一行,并且有个很酷炫的名字——lambda,没错,这就是匿名函数
匿名函数的关键字是 lambda,之后是一系列的参数,然后用冒号隔开,最后则是由这些参数组成的表达式。我们通过几个例子看一下它的用法:
在这里插入图片描述

square = lambda x: x**2
square(3)
#输出结果:9

lambda *args: sum(args);输入是任意个数的参数,输出是它们的和

func = lambda *args: sum(args)
func( 2,4,6,8,10 )
#输出结果:30

python中高阶函数

map函数

map函数:map函数是相当于通过一个函数将输入变量一一映射(通过某类函数)进行输出。map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。map(函数 f, 序列 x):对序列 x 中每个元素依次执行函数 f,将 f(x) 组成一个「map 对象」返回 (可以将其转换成 list 或 set)

def func(x):
    return x*x
r = map(func,[1,2,3])
print(list(r))
#输出结果
[1, 4, 9]

与匿名函数结合

lst = [1, 2, 3, 4, 5]
map_iter = map( lambda x: x**2, lst )
print( map_iter )
print( list(map_iter) )
#输出结果
<map object at 0x00000000051FE978>
[1, 4, 9, 16, 25]

在 上面第二个map 函数中
第一个参数是一个计算平方的「匿名函数」
第二个参数是列表,即该「匿名函数」作用的对象
注意 map_iter 是 map 函数的返回对象 (它是一个迭代器),想要将其内容显示出来,需要用 list 将其转换成「列表」形式。

filter函数

Python内建的filter()函数用于过滤序列。和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。所以filter和map区别一点就是filter就像滤波器一样会保留或者丢掉一些元素,但是map是不会丢弃元素的,只是会作用在一些元素上面的。filter(函数 f, 序列 x):对序列 x 中每个元素依次执行函数 f,将 f(x) 为 True 的结果组成一个「filter 对象」返回 (可以将其转换成 list 或 set)

    def is_odd(num):
    #if(num%2==1):
    #return num
        return num % 2 == 1 # 这句话的含义就是先进行判断然后进行合适数字的输出
    print(list(filter(is_odd, [1, 2, 3, 4, 5, 6]))) # filter和map类似的,输出的也是一个惰性序列,需要通过List强制进行转换
#输出结果:[1, 3, 5]

与匿名函数结合

lst = [1, 2, 3, 4, 5]
filter_iter = filter(lambda n: n % 2 == 1, lst)
print( filter_iter )
print( list(filter_iter) )
#输出结果:[1, 3, 5]

第一个参数是一个识别奇数的「匿名函数」
第二个参数是列表,即该「匿名函数」作用的对象
同样,filter_iter 作为 filter 函数的返回对象,也是一个迭代器,想要将其内容显示出来,需要用 list 将其转换成「列表」形式。

reduce函数

reduce把一个函数作用在一个序列[x1, x2, x3, …]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)。reduce(函数 f, 序列 x):对序列 x 的第一个和第二个元素执行函数 f,得到的结果和序列 x 的下一个元素执行函数 f,一直遍历完的序列 x 所有元素。 reduce 函数,顾名思义就是累积函数,把一组数减少 (reduce) 到一个数。

from functools import reduce
def add(a,b):
    return a+b
print(reduce(add,[1,2,3,4])) # 使用redunce这里相当于是sum

def add1(a,b):
    return 10 * a + b
print(reduce(add1,[1,2,3,4]))
#输出结果:
10
1234

与lambda函数结合

from functools import reduce
lst = [1, 2, 3, 4, 5]
reduce( lambda x,y: x+y, lst )
#输出结果:15

在 reduce 函数中
第一个参数是一个求和相邻两个元素的「匿名函数」
第二个参数是列表,即该「匿名函数」作用的对象
在 reduce 函数的第三个参数还可以赋予一个初始值

from functools import reduce
lst = [1, 2, 3, 4, 5]
reduce( lambda x,y: x+y, lst,100)
#输出结果:115

偏函数

偏函数 (paritial function) 主要是把一个函数的参数 (一个或多个) 固定下来,用于专门的应用上 (specialized application)。要用偏函数用从 functools 中导入 partial 包:

from functools import partial
lst = [3, 1, 2, 5, 4]
sorted( lst )
#输出结果:
[1, 2, 3, 4, 5]

sort 函数默认是按升序排列,假设在你的应用中是按降序排列,你可以把函数里的 reverse 参数设置为 True。

    from functools import partial
    lst = [3, 1, 2, 5, 4]
    sorted( lst,reverse=True)
    #输出结果:
  [5, 4, 3, 2, 1]

每次设定参数很麻烦,你可以专门为「降序排列」的应用定义一个函数,比如叫 sorted_dec,用偏函数 partial 把内置的 sort 函数里的 reverse 固定住,代码如下:

sorted_dec = partial( sorted, reverse=True )
st = [3, 1, 2, 5, 4]
sorted_dec(lst)
  #输出结果:
 [5, 4, 3, 2, 1]

嵌套函数

以普通的加法函数为例:

def add1(x, y):
    return x + y
def add2(x):
    def add(y):
        return x + y
    return add
g = add2(2)
print( add1(2, 3) )
print( add2(2)(3) )
print( g(3) )
#输出结果
5
5
5

解析式

解析式 是将一个可迭代对象转换成另一个可迭代对象的工具。不严谨地说,容器类型数据 (str, tuple, list, dict, set) 都是可迭代对象。
第一个可迭代对象:可以是任何容器类型数据。
第二个可迭代对象:看是什么类型解析式:
列表解析式:可迭代对象是 list
字典解析式:可迭代对象是 dict
集合解析式:可迭代对象是 set
下面写出列表、字典和集合解析式的伪代码 (pseudo code)。

#list 解析式
[值 for 元素 in 可迭代对象 if 条件]
#dict 解析式
{键值对 for 元素 in 可迭代对象 if 条件}
#set 解析式
{值 for 元素 in 可迭代对象 if 条件} 

列表解析式:从一个含整数列表中把奇数 (odd number) 挑出来,我们先用for语句来实现。

lst = [1, 2, 3, 4, 5] 
odds = []
for n in lst:
    if n % 2 == 1:
        odds.append(n)
print(odds)
#输出结果:[1, 3, 5]

然后用列表解析式来实现:

lst = [1, 2, 3, 4, 5] 
odds = [n for n in lst if n % 2 == 1]
print(odds)
#输出结果:[1, 3, 5]

我们
在这里插入图片描述
问题:用解析式将二维元组里每个元素提取出来并存储到一个列表中。
tup = ((1, 2, 3), (4, 5, 6), (7, 8, 9))
先遍历第一层元组,用 for t in tup,然后遍历第二层元组,用 for x in t,提取每个 x 并"放在“列表中,用 []。代码如下:

tup = ((1, 2, 3), (4, 5, 6), (7, 8, 9))
flattened = [x for t in tup for x in t]
print(flattened)
#输出结果:[1, 2, 3, 4, 5, 6, 7, 8, 9]

字典解析式
在这里插入图片描述
集合解析式
在这里插入图片描述
解析式与高阶函数对比:
用「在列表中先找出奇数再乘以 2」的例子,对于列表 lst = [1, 2, 3, 4, 5],我们先看「列表解析式」的实现:

lst = [1, 2, 3, 4, 5]
[ n*2 for n in lst if n%2 == 1]
#输出结果:[2, 6, 10]

再看「map/filter」的实现:

lst = [1, 2, 3, 4, 5]
list( map(lambda n: n*2, filter(lambda n: n%2 == 1, lst)) )
#输出结果:[2, 6, 10]

谁简谁繁,一目了然。

总结

函数包括自定义函数 (用 def) ,匿名函数 (用 lambda)还有高阶函数(map,reduce,filter),函数的参数形态也多种多样,有位置参数、默认参数、可变参数、关键字参数、命名关键字参数。
解析式并没有解决新的问题,只是以一种更加简洁,可读性更高的方式解决老的问题。解析式可以把「带 if 条件的 for 循环」用一行程序表达出来,也可以实现 map 加 filter 的功能。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值