在之前的章节中,针对各类对象调用了非常多的函数,这些都是Python的内建函数。这些函数的功能都是预先设计好的,但在实际生产过程中,使用最多的还是自定义函数。

一、函数的定义和调用

创建函数的目的是封装业务逻辑,实现代码的重用。本小节主要介绍如何创建和调用函数。

1.创建函数

一个完整的函数由函数名、参数列表、返回值构成。创建函数有以下规则。
(1)一个函数若是没有名称,则称为匿名函数。
(2)Python中使用def关键字定义命名函数。
(3)函数可以没有参数,也可以有多个不同类型的参数。
(4)函数可以有多个返回值,也可以不指定返回值,此时默认的返回值为None。
(5)在其他语言中,有函数签名和函数定义的区别,而在Python中,这两个概念统一后,即一个没有具体业务逻辑的函数,在代码块中使用" pass "代替。
创建函数的方式,如下

def test():
    """
    这是一个没有方法体的函数,使用pass占位符代替代码块
    :return:
    """
    pass

def add(item):
    """"
    该函数有参数item,并使用return返回函数处理结果
    :param item:
    :return:
    """
    return 5 + item

def show(item):
    """
    该函数有方法体,但是没有指定返回值,返回None
    :param item:
    :return:
    """
    5 + item
test()
print("调用add方法的返回值:",add(5))
print("调用show方法的返回值:",show(5))

执行结果如下

调用add方法的返回值: 10
调用show方法的返回值: None
2.调用函数

与大多数编程语言一样,通过函数名加一对小括号并正常传递参数,即可完成函数调用。需要注意的是,函数调用需要在函数申明之后,否则会触发异常,如下

def add(item):
    print("在add中调用sub:",sub(item))
    return 5 + item

try:
    sub(5)
except Exception as e:
    print("在函数申明前调用,触发异常:",e)

def sub(item):
    return 10 - item

print("调用add方法:",add(5))

执行结果如下,为什么在第6行调用了sub报错,而第2行调用sub就能正常执行了呢?原因是代码是从上往下解析的,调用sub时,sub方法还未创建。sub方法是在第10行创建的,在第13行调用add方法时,sub方法已经创建完成,因此得到正确的结果。

在函数申明前调用,触发异常: name 'sub' is not defined
在add中调用sub: 5
调用add方法: 10

二、函数的参数

Python函数的参数具有灵活性,其定义的方法可以接受各种形式的参数,也可以简化函数调用方法的代码。

1.位置参数

如下,func1函数具有1个位置参数,func2函数具有2个位置参数。在对函数进行调用的时候,有几个位置参数就需要传递几个参数,否则会触发异常。并且,传入参数与函数参数列表是一一对应的,如第13行,a等于10,b等于20。

def func1(a):
    print("输出位置参数a的值:",a)
    return a


def func2(a,b):
    print("输出位置参数a:%a,b:%s"%(a,b))
    return a+b


print("函数调用func1(10):",func1(10))
print("")
print("函数调用func2(10,20):",func2(10,20))

执行结果如下

输出位置参数a的值: 10
函数调用func1(10): 10

输出位置参数a:10,b:20
函数调用func2(10,20): 30
2.可选参数

如下,可选参数是指带有默认值的参数,在对该函数进行调用的时候,可以不必显示传递该函数。当不传递默认函数,函数将使用默认值,如第6行;若传递默认函数,函数将使用传入的值,如第8行。可选参数常用于修改一个现有的函数,避免该函数在其他调用的地方出错。

def func2(a,b=5):
    print("输出位置参数a:%a,可选参数b:%s"%(a,b))
    return a+b


print("调用时传递1个参数func2(1):",func2(1))
print("")
print("调用时传递2个参数func2(1,2):",func2(1,2))

执行结果如下

输出位置参数a:1,可选参数b:5
调用时传递1个参数func2(1): 6

输出位置参数a:1,可选参数b:2
调用时传递2个参数func2(1,2): 3

可选参数在定义的时候,需要写在位置参数的后面,否则会报错。另外,在设计多个可选参数的时候,建议将经常变化的参数写在前面,长期不变的写在后面。在调用有多个可选参数的函数是,参数的对应关系是优先根据传递位置来决定的,除非显示指定参数名。若是没有传递的参数,该参数仍然使用默认值,如下

def func2(a,b=5,c=6,d=7):
    print("输出位置参数a:%a,可选参数b:%s,可选参数c:%s,可选参数d:%s"%(a,b,c,d))
    return a+b+c+d


print("参数按顺序赋值:",func2(1,2,3,4))
print("")
print("参数按指定名称赋值:",func2(1,d=2,b=4))

执行结果如下

输出位置参数a:1,可选参数b:2,可选参数c:3,可选参数d:4
参数按顺序赋值: 10

输出位置参数a:1,可选参数b:4,可选参数c:6,可选参数d:2
参数按指定名称赋值: 13

对参数提供默认值,可以提高调用方代码的灵活性,但也有可能出现bug,如下

def func(a=[]):
    a.append("hello")
    print("a是一个列表",a)


func()
func()
func()

执行结果如下,调用方认为每次都只应该输出一个" hello ",然而实际上输出的内容会随着调用次数的增加而增加。原因是在创建函数时,a对象就已经被创建好了,等于一个空列表。在调用过程中修改了a对象,那么a的默认值就变了,因此每次调用a对象都会使用上次调用改变后的值。

a是一个列表 ['hello']
a是一个列表 ['hello', 'hello']
a是一个列表 ['hello', 'hello', 'hello']

解决办法是将默认参数修改为不可变对象,如下

def func(a=None):
    if a is None:
        a=[]
    a.append("hello")
    print("a是一个列表",a)


func()
func()
func()

执行结果如下

a是一个列表 ['hello']
a是一个列表 ['hello']
a是一个列表 ['hello']
3.可变参数与关键字参数

可变参数是指在传递参数时,可以传递任意个数的参数;关键字参数是指可以传递任意个包含名字的参数。如下,func1函数定义的可变参数,适用于传递列表、元组,使用" * “;func2函数适用于传递字典,字典中的键名即为参数名称,使用” ** ",这一点和参数解包的逻辑是一样的。

def func1(*args):
    count=0
    for i in args:
        count=count + i
    return  count


def func2(**kwargs):
    tmp_list=[]
    for k,v in kwargs.items():
        tmp_list.append("key:%s value:%s"%(k,v))
    return tmp_list


data = [1,2,3,4,5]
print("传递可变参数:",func1(*data))
data=(1,3,5)
print("传递可变参数:",func1(*data))
dic={"key1":1,"key2":2}
print("传递关键字参数:",func2(**dic))

执行结果如下

传递可变参数: 15
传递可变参数: 9
传递关键字参数: ['key:key1 value:1', 'key:key2 value:2']

以上是对于已有列表、元组、字典对象进行传递,还可以不使用" * ",直接传递,如下

print("传递可变参数:",func1(1,2,3,4,5))
print("传递可变参数:",func1(1,3,5))
print("传递关键字参数:",func2(key1=1,key2=2))

如下,第2行参数列表中有一个" * “,之后的参数就是关键字参数,其名称是” c " 和" d “,调用方式如第12行所示。当” * “后面紧接着名称,如第7行,则不必单独写一个” * "。

def func1(a,b=5,*,c,d):
    print("输出位置参数a:%a,可选参数b:%s,\n命名关键词参数c:%s,命名关键字参数d:%s"%(a,b,c,d))


def func2(a,b=5,*args,c,d):
    print("输出位置参数a:%a,可选参数b:%s,\n命名关键词参数c:%s,命名关键字参数d:%s"%(a,b,c,d))


func1(10,c=11,d=12)
print("")
func2(10,c=11,d=12)

执行结果如下

输出位置参数a:10,可选参数b:5,
命名关键词参数c:11,命名关键字参数d:12

输出位置参数a:10,可选参数b:5,
命名关键词参数c:11,命名关键字参数d:12

三、函数的返回值

Python中的函数可以使用return返回数据,也可以不用return返回,则默认返回" None "。在Python中,所有事物都被当做对象,函数也是一个对象,因此函数的返回值可以是另一个函数。

1.返回零到多个值

如下,func1函数,没有使用return返回数据,因此默认返回值为" None "。func2函数有多个返回值,默认情况下,这些返回值将构成一个元组进行返回,因此在倒数第5行,变量a是一个元组类型。func2函数返回了两个值,因此在倒数第2行定义了两个变量,分别接收这两个值,这成为返回值解包。

def func1():
    print("调用func1函数:")


def func2(item1,item2):
    return item1 +5,item2 +6


a=func1()
print("func1返回值a的值为:",a,end="\n\n")
p1={"item1":10,"item2":11}

print("调用func2函数:")
a=func2(**p1)
print("func2返回值a的类型为:",type(a))
print("func2返回值a的值为:",a)
a,b=func2(**p1)
print("func2返回值解包,a是:%s,b是:%s"%(a,b))

执行结果如下

调用func1函数:
func1返回值a的值为: None

调用func2函数:
func2返回值a的类型为: <class 'tuple'>
func2返回值a的值为: (15, 17)
func2返回值解包,a是:15,b是:17
2.返回函数

如下,在fun1函数中返回了fun2的函数名称。那么在第7行,变量fun就是一个函数对象,因此输出fun的类型为<class’function’>。既然fun是函数,那么在fun后面加一对小括号就能对其进行调用,如第9行

def fun1():
    def fun2():
        return 10

    return fun2

fun=fun1()
print("fun1返回值类型:",type(fun))
result=fun()
print("fun()调用结果为:",result)

执行结果如下

fun1返回值类型: <class 'function'>
fun()调用结果为: 10

四、全局变量和局部变量

全局变量和局部变量是一个相对的概念,在函数内部的变量,称为局部变量;在该函数外的变量,称为全局变量。若是需要将变量对所有函数可见,则需要加global关键字进行修饰。如下,global将var1、var2、var3都变成了全局变量,因此在fun方法外,还能正常访问,访问变量var4,程序则会触发异常。

def fun():
    global var1,var2,var3
    var1=100
    var2=200
    var3=300
    var4=400
    print("fun中的局部变量var1:%s,var2:%s,var3:%s,var4:%s"%(var1,var2,var3,var4))


fun()
print("全局变量var1:%s,var2:%s,var3:%s"%(var1,var2,var3))
try:
    print("输出变量var4:%s"%(var4))
except Exception as e:
    print("异常信息:",e)

执行结果如下

fun中的局部变量var1:100,var2:200,var3:300,var4:400
全局变量var1:100,var2:200,var3:300
异常信息: name 'var4' is not defined

注意,在Python中,使用global方法,可以获取当前程序内部所有的全局变量
本篇文章就到这里结束了,希望能给小伙伴们一些帮助,喜欢的小伙伴们可以三连支持一下博主!!

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐