一、前言
本小节主要梳理函数的基本知识,包含函数的定义和作用、参数的类别、return语句、变量作用域等。另外拓展函数的三种展现形式:匿名函数、闭包、装饰器。
环境说明:Python 3.6、windows11 64位
二、函数介绍
2.1 函数是什么
函数是组织好的、可以重复使用的、用来实现单一功能的代码。由于函数的这些特性,使得主体代码可以变得更加的简洁,一些功能可以反复调用,减少重复写代码,代码之间的耦合度也降低,降低维护难度,提高效率。
基本语法:
def 函数名(参数1,参数2,...参数n):
函数体
注意点:
- 函数名和变量名的命名逻辑差不多,不能数字开头,多个词关联只能用下划线,函数名也可以用中文命名!命令时尽量和所实现的功能相近,特别在函数比较多的情况下,方便理解。
- 参数的类别比较多,有位置参数,默认参数,不定长参数,关键参数(下文逐一说明)。
- 函数体是所有实现功能的代码。
写一个简单函数(如下),该函数就是把Hello world!封装起来,每次我们调用func()的时候就可以直接打印Hello world!
了,是不是很简单。
# 定义函数
def func():
print('Hello world!')
# 调用
func()
当然,如果仅仅是打印Hello world!
大可不必这么大费周章,但是如果是其他很复杂的一些逻辑就大有裨益。
像很多Python库,基本都是已经封装好的,然后暴露相关可以完成某些工作的接口,这些接口的名称,往往对应着某个底层的函数,贡献者把相关的代码封装起来,然后留一个函数名给使用者进行调用。最基本的就是Python的内置函数,如str()、int()、float()等。
2.2 函数的参数
参数的类别比较多,有位置参数,默认参数,不定长参数,关键参数。
- 位置参数:就是跟参数所在的位置相关,涉及到参数的顺序,其实有点像列表或元组,每一个参数有一个位置标签,传入参数时按照位置标签一一匹配。如下代码,我创建了一个叫
Say_Hello
的函数,函数有两个参数:greet
和objects
,greet是问候语变量,objects是问候对象变量。我后面调用的时候,按顺序传入('Hello','world')
就可以打印出Hello world!
。如果我改用Say_Hello('world','Hello')
是否可以呢?大家可以试试。代码不会报错,但是结果便反过来了,变成了world Hello!
。- 注:除了按顺序赋值,还可以通过指定参数名,如下最后2行
def Say_Hello(greet, objects): # 定义函数
print('%s %s!'%(greet, objects))
Say_Hello('Hello','world') # 该句为调用函数,打印结果为:Hello world!
Say_Hello(greet='Hello',objects='world')
Say_Hello(objects='world',greet='Hello')
- 默认参数:给参数赋一个值,作为默认值,如果不传参数则采用默认值。注意,默认参数要放在位置参数后面。如下代码,给
greet
加上一个默认值,则参数要放在objects
后面,传参时可以不用传,也可以重新传一个覆盖已有的值。
def Say_Hello(objects,greet='Hello'):
print('%s %s!'%(greet, objects))
Say_Hello('world') # 打印结果为:Hello world!
Say_Hello('world','Hi') # 打印结果为:Hi world!
- 关键参数:就是
**kw
。可以不用传值,也可以通过参数=值
的形式传递一个或多个参数。具体看看以下例子。
def Say_Hello(objects,greet='Hello',**kw):
print('%s %s! %s'%(greet, objects,kw))
Say_Hello('world','Hi',my_name='Xindata') # 打印结果为:Hi world! {'my_name': 'Xindata'}
Say_Hello('world','Hi',my_name='Xindata',other='test') # 打印结果为:Hi world! {'my_name': 'Xindata', 'other': 'test'}
- 不定长参数:不限制参数个数,可以传一个或多个,以元组形式返回。返回的时候也是以元组的格式返回。如果要将元组元素分别取出来,可以使用
join()
。
def Say_Hello(greet, *objects):
print('%s %s!'%(greet, objects))
# print('%s %s!'%(greet,'、'.join(objects)))
Say_Hello('Hello','world') # 打印结果为:Hello ('world',)!
Say_Hello('Hello','world','Xin学数据') # 打印结果为:Hello ('world', 'Xin学数据')!
2.3 函数体
函数体是所有实现功能的代码。函数体中可以通过return
返回一个值,在函数调用的时候,可用于赋值给变量;当不需要返回值时,只是实现某项功能时,也可以不用加return
语句。
前面讲列表排序的时候涉及到两个函数:list.sort()
和sorted(list)
。前者没有返回值,仅对列表的顺序进行改变,所以使用sort()
函数之后,原来的列表的元素顺序便改变了,但是没有返回任何值(函数没有返回值,如果用于赋值,打印出来的变量是None
)。后者有返回值,调用该函数之后,不会对原来的列表的元素的顺序进行更改,而是返回一个新的列表,这个新的列表就是sorted()
函数的返回值。
ls = [3,7,0,5]
ls1 = sorted(ls)
print(ls) # [3, 7, 0, 5]
print(ls1) # [0, 3, 5, 7]
ls2 = ls.sort()
print(ls) # [0, 3, 5, 7]
print(ls2) # None
2.3.1 return
函数体遇到return
之后就停止执行并将结果返回。当一个函数没有return
时,返回None
。
def Hello(words):
return words
print('该句不打印!')
val = Hello('Hello world!')
print(val) # 打印结果为:Hello world!
2.3.2 变量作用域
函数外定义的变量属于全局变量,在函数内部可以进行调用。
my_name = 'Xindata'
def Hello():
print('My name is %s.'%my_name)
Hello() # 结果为:My name is Xindata.
print(my_name) # 结果为:Xindata
函数内部定义的变量属于局部变量,在函数外部调用该变量会报错。
def Hello():
my_name = 'Xindata'
print('My name is %s.'%my_name)
Hello()
print(my_name) # 报错NameError: name 'my_name' is not defined
常见的错误:在全局定义一个变量,然后再函数内部使用该变量,然后又赋值给一个名字相同的变量。(如下num)
num = 1
def test():
num += 1
print(num)
test()
那想要将函数内部的变量给到全局使用怎么办?用global
。举个例子:
def Hello():
global my_name
my_name = 'Xindata'
print('My name is %s.'%my_name)
Hello() # 结果为:My name is Xindata.
print(my_name) # 结果为:Xindata
2.4 另外三种展现形式
除了上面讲的比较常规的展现形式,还有一些其他的展现形式,这里主要讲三种:匿名函数、闭包、装饰器。
2.4.1 匿名函数
匿名函数的关键字是lambda
。一般用于实现一些比较简单的处理。
比如将上一个代码用lambda
实现如下:
# 原代码
def Hello(words):
return words
val = Hello('Hello world!')
print(val) # 打印结果为:Hello world!
# lambda 实现
Hello = lambda words:words
val = Hello('Hello world!')
print(val) # 打印结果为:Hello world!
lambda
函数的格式是在lambda
后面跟参数,同普通函数的参数,然后冒号,加上处理逻辑即可。
lambda [参数]:[处理逻辑]
lambda
函数用于处理数据会比较多一些,像用pandas或numpy处理数据的时候,经常要传入一个简单的函数处理数据,通过lambda
处理会比较简洁、方便。
import pandas as pd
dic = {'A':[1,3,4,2],'B':[8,3,9,5]}
df = pd.DataFrame(dic)
print(df)
df['AA'] = df.A.apply(lambda x:x**2) # 将A列的值进行平方,然后在df中新增AA列来存放
print(df)
2.4.2 闭包
闭包就是函数里再嵌入一个函数,内层函数将结果返回给外层函数,调用外层返回内层函数(是返回,而不是调用,不需要加括号)。
作用:将函数作为返回值,传递函数。需要查看函数结果时再进行调用打印。
def Introduction(greet,*words):
def My_Introduction():
my_words = ' '.join(words) # 将传递给words 的值用空格连接起来
return '%s,%s!'%(greet,my_words)
return My_Introduction # 注意,没有括号,调用Introduction()时返回My_Introduction 函数
aa = Introduction('Hello','My name','is','Xindata') # 外层return 返回值(My_Introduction函数)赋值给变量aa ,此时aa 是一个函数类型的变量
bb = aa() # 调用aa() 函数,将内层return 返回值(字符串)赋值给变量bb
print(type(aa)) # 结果为:<class 'function'>
print(type(bb)) # 结果为:<class 'str'>
print(aa) # 结果为:<function Introduction.<locals>.My_Introduction at 0x000001F6D5FC0A60>
print(bb) # 结果为:'Hello,My name is Xindata!'
2.4.3 函数装饰器
装饰器的作用是给函数增加新的功能,但又不修改原来的函数,而是使得调用原函数的时候附加一些功能。
装饰器一般是一个闭包函数,只是函数接收的参数是一个函数。如下代码,Self_Introduction()
就是一个装饰器,直接调用即可。
# 定义一个装饰器
def Self_Introduction(func): # 传入被装饰的函数
def wrapper():
print('print wrapper')
return func() # 返回被装饰函数的调用结果,如果传递的func函数没有return,则结果为None,此时return 可以去掉。但是由于装饰函数一般需要具备一定的普适性,所以习惯加上return 。
return wrapper # 返回wrapper 函数
def Introduction():
global name
name = 'Xindata'
print('My name is %s!'%name) # 在装饰器函数中调用的时候才执行,如果装饰器没有调用,则不执行
return 'name:%s'%name # 调用Introduction() 时的返回值
## 调用函数
aa = Self_Introduction(Introduction) # 将wrapper 函数赋值给aa
print(aa) # 结果为:<function Self_Introduction.<locals>.wrapper at 0x0000019A7956F9D0>
bb = aa() # 调用aa() 函数,结果为:print wrapper【wrapper()函数print()结果】 和 My name is Xindata!【调用func()的结果】。当被修饰的函数(这里是Introduction()有返回值时,该返回值会赋值给bb,传递过程是通过wrapper 函数调用func(),func()执行之后返回Introduction()的return 值给到wrapper的return值,然后在调用aa函数时传递给bb)
print(bb) # 结果为:name:Xindata
print(name) # 结果为:Xindata。如果wrapper中没有调用func()则报错。
装饰器除了像以上展示代码,通过调用装饰器,传入函数的方法,还可以用@
语法糖,然后调用被装饰的函数来实现。具体如下代码:
def Self_Introduction(func):
def wrapper():
print('print wrapper')
return func()
return wrapper
@Self_Introduction # 在Introduction前,用@Self_Introduction声明用Self_Introduction 装饰Introduction
def Introduction():
global name
name = 'Xindata'
print('My name is %s!'%name)
return 'name:%s'%name
## 调用函数
aa = Introduction # 直接将Introduction 赋值给aa,相当于早期方法调用了Self_Introduction(Introduction),将wrapper 函数赋值给aa
print(aa)
bb = aa()
print(bb)
print(name)
更详细的用法可参考Python 进阶:函数装饰器。
小结
python函数是一个很有用的工具,可以解决很多重复性的工作。它就好比是我们经常干的一件事,把一些经常要用的东西做成模板,之后每一次使用时,创建一个副本即可。
本文的知识架构如下:
<下节预告:类和实例>
- End -