关注微信公众号IT小组,获取更多知识干货~
目录:
函数的基本概念
递归函数
局部变量、全局变量、函数多个return语句
匿名函数
高阶函数
装饰器
一:函数的基本概念
任何编程语言中,函数是经常被使用到的,在Python中也不例外。
例如我们第一次接触到打印语句print时,print是Python中的函数。在使用print函数时,只需要给出要打印的数据,无论是变量,表达式还是字符串。print函数便都能将其打印出来。
函数是对语句的一种封装。
因为我们将很多语句写在函数中,当需要使用的时候,通过调用这个函数来使用函数内部的语句代码。
在Python中使用“def”关键字来定义函数,例子如下:
def hello(): # Python中以def来作为定义函数的关键字
print ( 'hello' )
hello() # 调用函数的方式是函数名(),如果有参数则需要传递参数,下面会介绍
# 打印结果如下:
hello
从上面我们知道了,函数定义后,并不会自动执行,只有调用了函数,函数才会执行。函数是调用一次执行一次。
函数的命名规则和之前的变量命名规则一致,不过函数名的规范一般是动词加名词。
(在Python中,变量名,函数名,类名,模块名,都可以称为标识符,它们的命名是有一定规则的。Python的标识符命名规则和绝大多数高级编程语言一样,都是以数字和字母下划线构成,但是不能使用关键字和以数字开头并且区分大小写。——Python(1)——初识Python)
上面自定义的hello函数,在调用时没有传递任何的参数。但是很多函数都会要求传入参数,比如前面章节中我们调用len函数的时候,要给定一个可迭代对象作为参数,如下:
print ( len( 'hello' ) )
# 打印结果如下:
5
如果想让之前定义的hello函数接收参数,根据参数来打印结果。可以给函数增加形参,(在函数( )里面的变量称为形参,调用函数时传入的参数称为实参)。
代码如下:
def hello(name,place): # 在函数( )里面的name,place变量我们称为形参
print ( 'hello,'+ name )
print ( 'welcom to ' + place )
hello( 'xiaoming' , 'shanghai' ) # 在调用函数时传递的参数,我们称为实参
# 打印结果如下:
hello,xiaoming
welcom to shanghai
# 我们还可以定义如下的函数,add(),让两个数相加,如下:
def add( a , b ):
print ( a + b )
add ( 2 ,3 )
add ( 'nihao' , 'xiaoming' )
# 打印结果如下:
5
nihaoxiaoming
函数中参数传递方式和前面字符串方法format参数的传递方式一致(Python(5)——Python中字符串的format方法)分为了位置参数,关键字参数等等,代码如下:
def add ( a , b ): # 自定义add函数和两个形参a,b
print ( a + b )
c = add ( 1, 1 ) # 这是位置传参,参数按照顺序来传递
d = add ( a=1,b=2 ) # 这是关键字参数
e = add ( 2, b=1 ) # 位置参数和关键字参数混合使用,但是位置参数要在关键字参数前
函数的返回值:
自定义的add函数中,虽然可以通过函数内部的print函数语句来进行数据的处理和输出,看似add函数是有返回值的,但其实add函数并没有返回值,代码如下:
def add( a , b ):
print ( a + b )
a = add ( 2 , 3 ) # 通过变量a来获取add的返回值
print ( a )
# 打印结果如下:
5 # 这是add函数中print函数语句的打印出来的
None # 这才是自定义函数add的返回值,前面我们提到过,当函数没有返回值时会返回None
自定义add函数没有返回值,意味着我们对add函数处理后的数据操作有限,比如我们想调用add函数后再对add函数输出的数据的进行操作时,会非常的麻烦。要想对此数据进行处理则需要修改add函数才可以,显然这样对于简单的函数可以,但是当函数内部的结构过于庞大的时候,这样就太过于麻烦。为此,我们可以使用return语句给函数增加返回值,通过return语句来控制函数的返回值,代码如下:
def add( a , b ):
return ( a + b ) # return语句是用来返回值的
a = add ( 1 ,3 ) # 因为add函数有return语句,会将return返回的值存储到变量a里
print ( a )
# 打印结果如下:
4
函数默认参数,代码例子如下:
def hello( name,place='shanghai' ): # place给定了默认参数shanghai
print('{},welcom to {}'.format(name, place))
hello('xiaowang') # 只给了name参数,place参数未给,上面place参数默认为shanghai
# 打印结果如下:
xiaoming,welcom to shanghai
函数还可以传入可变参数,上面函数中的参数个数都是固定的,而Python使用*args来存储多余的参数并以元组保存,如下:
def hello( name,place='shanghai', *args ):
print('{},welcom to {},args的参数是{}'.format(name, place,args))
hello( 'xiaohong','beijing','xiaoming','xiaolan' ) # 超出了函数形参的个数
# 打印结果如下:
xiaohong,welcom to beijing,args的参数是('xiaoming', 'xiaolan')
通过*args可以对传入多余的参数进行保存,但是对函数已有的形参不能修改,如果想对函数已有的形参进行修改,可以使用**kwargs变量,**kwargs变量中的数据以字典形式保存,代码例子如下:
def hello( name,place,**kwargs ):
print('{},welcom to {},新增变量是{}'.format(name, place,kwargs))
hello( 'xiaohong' , 'shanghai' , age = 18 )
# 打印结果如下:
xiaoming,welcom to shanghai,新增变量是{'age': 19}
二:递归函数
在自定义函数并调用函数的时候,代码例子如下:
def add( a , b ) :
print ( a + b ) # 调用了print函数,并且传入了参数
add() # 调用add函数
上述自定义了一个函数add,用于两个数值的相加,在add 函数中,我们发现了add函数内部有print函数(调用了print函数),这也就意味着函数内部可以调用其他的函数。
如果一个函数内部调用自己,我们称为递归函数,代码例子如下:
def hello():
print ( 'hello,world' )
hello() # 调用自己
hello()
# 打印结果如下:
hello,world
hello,world
hello,world
hello,world
hello,world
hello,world
....
# 在hello函数内部自己调用自己,会不停的调用下去,直到内存溢出,产生错误
# 在上面hello函数之所以会不停的递归下去,是因为它的内部没有条件使得递归条件为False
在递归函数内部,要留一个条件,使得递归满足条件才能执行,我们称为出口。递归函数不利于理解,但是对于某些特定的问题,使用递归会更加的简洁,如下:
# 求1+2+3+4一直加到n的和,如果不使用递归也可以用for in语句等完成,但是使用递归会更加的简洁,如下:
def add( n ):
if n == 0:
return 0
return n + add( n - 1 )
a = add( 5 )
print ( a )
# 打印结果如下:
15
上面自定义add函数是递归函数,出口是n0的时候,如果n!=0,则会一直调用自己,并把n作为参数传递进去,直到n0条件成立并返回值为0时结束。详细解释如下:
# add(5)将5传递到add里面的形参n,n=5,在判断n是否等于0,很明显
# 是否,然后,执行return n + add(n-1),意味着:
# add(5) = 5+add(5-1)
# add(4) = 4+add(4-1)
# add(3) = 3+add(3-1)
# add(2) = 2+add(2-1)
# add(1) = 1+add(1-1)
# add(0) = if n==0 : return 0
# 即add(5)=5+4+3+2+1
我们还可以使用递归实现最经典的斐波那契数列{1,1,2,3,5,8,13,21,34,55…},代码如下:
def fib( n ):
if n == 1:
return 1
if n == 2:
return 1
return fib(n-1) + fib(n-2)
print(fib(10))
# 打印结果如下:
55
递归虽然使得代码更简洁,但是却不利于阅读,在开发过程中,使用循环也同样能达到递归的效果,如下,使用循环来实现斐波那契数列:
# 1,1,2,3,5,8,13,21,34,55...
def fibonacci( n ):
num1 = num2 = 1
i = 0
while i < n - 1:
num1,num2 = num2,num1 + num2
i += 1
return num1
print ( fibonacci( 10 ) )
# 打印结果如下:
55
三:局部变量、全局变量、函数的多个返回值
全局变量和局部变量:
全局变量:定义在函数外部的变量称为全局变量,它在整个文件中都能被访问。
局部变量:在函数中定义的变量称为局部变量,只能在函数内部访问。
代码示例如下:
a = 100
def test():
a = 10
print ( 'test中的的a值是{}'.format( a ) )
test()
print ( '全局变量a的值是{}'.format ( a ) )
# 打印结果如下:
test中的的a值是10
全局变量a的值是100
上面可以知道,局部变量和全局变量不会干扰,如果想在函数中修改全局变量,需要使用global关键字。代码如下:
a = 100
def test():
global a = 10 # 通过global关键字修改了全局变量a的值
print ( 'test中的的a值是{}'.format( a ) )
test()
print ( '全局变量a的值是{}'.format ( a ) )
# 打印结果如下:
test中的的a值是10
全局变量a的值是10
locals,globals是Python中的内置函数,locals用来获取函数的所有的局部变量,globals用来获取所有的全局变量,数据都以字典的形式保存。代码如下:
a = 100
b = 12
c = 34
def a():
d = 10
e = 43
print ( locals() ) # 用locals内置函数用来打印局部变量
a() # 调用a函数来获取所有的局部变量
print ( globals( ) )
# 打印结果如下:
{'d': 10, 'e': 43} # a函数里面的局部变量
{ 'a': <function a at 0x0000000001D0C1E0>, 'b': 12, 'c': 34} # 全局变量
# 上面a对应的值是内存中分配给函数a的地址空间,(函数也是一种特殊的全局变量)
(在Python中只有函数可以分割作用域,而循环、选择语句中都不能分割作用域。作用域是一个可访问的资源范围)
函数的多个返回值:
函数是用return语句来返回值的。
def add( a , b ):
return a,b,a+b # 一般返回是一个元组,此时可以不用加()
print( add( 1 , 3 ))
# 打印结果如下:
(1, 3, 4)
函数中有多个return语句时,但是一般情况下,只会执行一个,代码如下:
def a(num):
if num > 60:
return '你考及格了'
else:
return '你没有考及格'
print ( a (89) )
# 打印结果如下:
你考及格了
四:匿名函数
在Python中有匿名函数,匿名函数使用lambda关键字来标识,匿名函数是当函数的功能非常简单的时候,可以考虑使用匿名函数,代码如下:
a = lambda name,age: 'hello,{},you age is {}'.format( name,age ) # 变量a被赋值成为一个匿名函数lambda
# lambda格式:lambda 变量,变量N : 代码语句
print ( a ( 'lisi' , 23 ) )
# 打印结果如下:
hello,lisi,you age is 23
# 函数也可以当成参数传递:
def add(a, b,fn):
return fn( a , b)
print (add ( 1,3,lambda a,b: a*b ) ) # 使用匿名函数还可以重写之前的函数
# 打印结果如下:
3
五:高阶函数
函数中可能会包含了其他的函数, 如果想对函数中的函数(局部函数)进行调用可以使用函数( )( ),代码如下:
def test2()
print ( 'test2函数运行了' )
def test():
print ( 'test函数运行了' )
return test2
a = test() # a获取test函数的返回值,这里test函数返回值是test2函数
a() # 因为test函数返回test2给变量a,而a()其实就是test2()
# 打印结果如下:
test函数运行了
test2函数运行了
# 在函数内部的函数(局部函数),如下:
def a():
x = 100 # 局部变量
print ( 'a函数运行了' )
def b(): # 局部函数
# x += 1 内部函数不能直接修改外部函数的变量,需要在外部函数的变量中声明为nonlocal
print ( 'b函数运行了' )
return b
# 在上面a函数中又包含了一个b函数,b函数只能通过a函数内部调用,如果想在外部访问,如下;
a()() # a()函数返回的是b函数,后面在加一个()意味着又调用了b函数
# 打印结果如下:
a函数运行了
b函数运行了
(如果想要在局部函数中访问上一层函数中的变量,则需要在变量前使用nonlocal关键字)
六:装饰器
装饰器是Python中独有的。
装饰器在Python中是非常重要的,利用装饰器可以将代码变的更加灵活和更加具有可扩展性。
Python中使用 “ @ ” 作为装饰器的关键字。
在了解装饰器之前,先考虑一个项目需求场景:“要求利用自定义函数算出从0-99的和。”
这是很简单的,代码如下:
def sum():
x = 0
for i in range( 100 ): # range类自动生成指定范围的数值
x += i
print( x )
sum()
# 打印结果如下:
4950
上面自定义了一个函数sum,在这个函数内部使用了for…in循环来求0-99的和,这是非常正确的。但是如果此时需求改变了:“在求0-99和之前先打印’ hello '字符串,并且不能修改sum函数,只允许手动调用sum函数来实现这个新增的需求。”
如何在不修改sum函数的基础上,增加新的功能呢?
此时装饰器的强大就体现出来了,装饰器既可以不修改原有代码,又能给原有代码增加新的需求实现,代码如下:
def hello(fn):
def active():
print( 'hello' )
fn() # fn存储了之前的sum函数,调用fn意味着调用sum函数
return active # 返回active函数,此时会执行active函数
@hello # 用hello装饰器装饰了sum函数
def sum():
x = 0
for i in range( 100 ):
x += i
print( x )
sum()
# 打印结果如下:
hello
4950
如上面代码所示,sum函数前面增加了一个 “ @hello ” 装饰器。
“@hello”本质上是一个函数,在调用sum函数的时候,会先调用装饰器,也就是hello函数,把sum函数当成参数传递到装饰器hello中,存放在fn中,并返回active函数。也就意味着sum函数被装饰成为了active函数,调用sum函数就是在调用active函数。如果想要调用sum函数,则需要调用fn。
此时可以看到,在未更改原有sum函数代码的情况下,增加了一个新的功能。
有参数的函数被装饰时:
之前的函数被装饰的时候,都没有参数,要是有参数的函数被装饰,代码如下:
def time(fn):
def active(a,b):
print('你的名字是{},你所在地点是{}'.format(a,b))
fn(a,b) # fn中存储之前的hello函数,需要注意此时fn中的形参要和active中的形参名称保持一致
return active
@time # 使用time装饰器装饰hello函数
def hello(name,place):
print ( '{}你好,欢迎来到{}'.format(name,place))
hello('lisi','shanghai') # 调用hello函数,
# 打印结果如下:
你的名字是lisi,你所在地点是shanghai
lisi你好,欢迎来到shanghai
如上面代码所示,hello函数传递了两个参数,所以装饰器time的内部函数active里面也要拥有两个形参用来接收,并且在调用hello函数时候,也要将参数传递过去。
装饰器本身也可以带参数,代码示例如下:
def time(n):
def hi(fn): # hi函数中的fn存储了hello函数
def active(a,b):
if n > 2017: # 如果time传递的实参大于2017则执行下面的代码
print('time的n参数是{}'.format(n))
print('你的名字是{},你所在地点是{}'.format(a,b))
fn(a,b) # fn则存储了hello函数,fn中的形参要和active形参名称保持一致
return active # 返回active函数
return hi
@time(2018) # 带参数的装饰器,2018这个实参被传递到time里的形参n中,而hello函数则传到了hi中的fn里
def hello(name,place):
print ('{}你好,欢迎来到{}'.format(name,place))
hello('lisi','shanghai')
# 打印结果如下:
time的n参数是2018
你的名字是lisi,你所在地点是shanghai
lisi你好,欢迎来到shanghai
装饰器time传递了一个实参过去,用装饰器time函数中的n来保存,而被装饰的hello函数则被传递到了hi函数的fn中来保存,并且返回了active函数,也就意味着下面调用hello函数就是调用hi函数中内置active函数,而调用fn则就是调用了hello函数。