微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

【Python基础】函数

一、函数定义

内建函数:内建函数也叫内置函数,即系统已经定义好的函数,开发者可以直接调用。类似pow(2,4)等等

1. 函数的定义

函数定义的一般格式如下:

def 函数名(参数列表):
    函数

例如:

def hello():
    print("hello")
    print("world!")

以上实例定义的hello()函数虽然不包含任何参数,但是函数名后的一对括号是不能省略的。在实际应用中,稍复杂的函数通常都会包含一个或多个参数。

以下代码定义了无任何操作的空函数nop()。

def nop():
    pass

在Python代码中,pass语句通常可以用来作为占位符,表示什么操作都不执行。比如在项目起始阶段,如果还没想好函数具体实现时,可以先放置一个pass语句,让代码先成功运行起来。待项目框架搭建完毕后,在进行相应的具体实现。

通常情况下,在Python语言中定义一个具有特定功能函数需要符合以下规则:

  • 函数代码块以def关键字开头,后接函数标识符名称和形参列表;
  • 任何传入的参数和自变量必须放在圆括号内;
  • 函数的第一行语句可以选择性地使用文档字符串(即函数说明);
  • 函数内容以冒号起始,并且严格统一缩进;
  • 函数都有返回值,认返回None。

2. 形参和实参

在编程语言中,函数定义时用的是形参,调用时用的是实参

  • 形参(parameter),全称为"形式参数",不是实际存在的变量,又称虚拟变量。形参是在定义函数名和函数体的时候使用的参数,目的是用来接收调用函数时传入的参数
  • 实参(argument),全称为"实际参数",是调用时传递给函数的参数。实参可以是常量、变量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。
  • 形参和实参的功能是数据传送。

以计算面积的函数为例:

# 计算矩形面积的函数area()
def area(width, height):
    return width * height

# 调用area函数
w = 4
h = 9
print("with=", w, "height=", h, "area=", area(w, h))

上述代码中,函数area()定义处的width和height就是形式参数,函数体外定义的变量w和h是实际参数。可以看到,把实参w和h传入函数体后,就把相应的值赋值给了形参width和height。形参width和height的作用域只限于area()函数体内,而实参w和h作用域则根据外部调用处的设置而定。

对于函数形参列表,认情况下函数调用时的参数值与参数列表声明中定义的顺序是一致。Python语言也允许函数调用时参数顺序与声明时不一致,即显示指明关键字参数,并根据参数的指定进行赋值。

def func(x, y):
    print('x+y=', x + y)
    print('x*y=', x * y)

# 等效于func(x=3,y=2),也等效于func(3,2)
func(y=2, x=3)

上述代码中,函数func()定义时形式参数的顺序是func(x, y),但是调用时实际参数的顺序确是func(y = 1, x = 2)。这是因为Python语言中提供了一种关键字参数的机制,可以给开发者提供更大的灵活性。

3. 函数的返回值

函数的返回值是函数执行完成后,系统根据函数的具体定义返回给外部调用者的值。
在Python语言中,当函数运行到return语句时即执行完毕,同时将结果返回。因此,可以在函数内部通过条件判断和循环设置实现较复杂的逻辑,并返回预期的结果。如果没有return语句,函数体内所有语句执行完毕后认返回None。

# 函数定义
def add(x, y):
    print('x+y=', x + y)
    return x + y

# 函数调用
result = add(y=1, x=2)
print(result)

上述代码中,定义的add()函数返回“x+y”的运算结果。可以看到,调用函数后,把该函数的返回值赋值给了变量result ,最后输出了变量result 的值。

另外需要注意的是,在Python语言中,函数也可以有多个返回值,例如:

# 函数定义
def add(x, y):
    print('x+y=', x + y)
    print('x*y=', x * y)
    return x + y, x * y

# 函数调用
a, b = add(y=1, x=2)
print(a, b)

上述代码中,定义的add()函数有连个返回值,分别是“x+y”和“x*y”。可以看到,调用函数后,把该函数的返回值分别赋值给变量a,b,最后输出了变量a和变量b的值。
注意: 返回值和接收变量的对应关系,是按照顺序一一对应的

二、函数分类

1. 内置函数

Python语言中自带函数叫做内建函数,这些内建函数对大部分常用操作进行有效封装,可以直接调用,为开发提供了极大便利。由于内建函数是Python语言内置的函数,因此不需要导入任何函数库即可直接调用

在Python语言中,还可以把函数名赋给一个变量,相当于给这个函数起了一个“别名”,如下代码所示。

a = abs
print(a(-1))

这里需注意,abs没有小括号,因此加了小括号相当于调用函数了。

2. 自定义函数

在Python语言中,内建函数可以直接使用,第三方函数需要使用import命令导入相应的库才能使用。对于自定义函数,其定义和调用可以在同一个文件中,也可分离成不同的文件

from test import hello

hello()

上述代码演示了函数的定义和调用不在一个文件的情形。首先,将hello()函数定义好并保存为test.py文件,然后使用Python语言的import指令“from test import hello”将该文件导入,可以调用hello()函数了。导入时需要注意test是文件名并且不含.py扩展名。

三、函数参数

1. 参数种类

函数参数分为可变类型和不可变类型,其调用结果是不同的。

  • 可变类型:类似c++的引用传递,如列表、字典等。如果传递的参数是可变类型,则在函数内部对传入参数的修改会影响到外部变量。
  • 不可变类型:类似c++的值传递,如整数、字符串、元组等。如果传递的参数是不可变类型,则在函数内部对传入参数的修改不会影响到外部变量。

想要查看一个变量是否可变,可以传入函数,该函数先打印形参id,然后在函数修改形参(整型、字符串可以再赋值,列表可以append…),再次打印形参id,对比两个id的值,相同则是可变类型,不同则是不可变类型

2. 认参数

编写函数时,可给每个形参指定认值。在调用函数时,如果给形参提供了实参,Python语言将使用指定的实参值;否则,将使用形参的认值。给形参指定认值后,可在函数调用中省略相应的实参。使用认值可简化函数调用,还可清楚地指出函数的典型用法。如下方式调用describe_student()函数会出现错误

def describe_student(person_name, student_age):
    "函数功能显示学生的信息"
    print("my name is ", person_name)
    print(person_name + "is" + student_age + "years old")

describe_student('Jack')

提示错误信息:

Traceback (most recent call last):
  File "D:/python_demo/demo_2.py", line 88, in <module>
    describe_student('Jack')
TypeError: describe_student() missing 1 required positional argument: 'student_age'

上述代码中,提示错误信息很明确,就是调用函数describe_student()时缺少了一个位置参数student_age。这个时候,认参数就排上用场了

若大部分学生的年龄为18岁,开发者可以把第二个参数student_age的认值设定为18,这样,当开发者调用describe_student(Jack)时,相当于调用describe_student(Jack,18) ,如下代码所示。

def describe_student(person_name, student_age='18'):
    "函数功能显示学生的信息"
    print("my name is ", person_name)
    print(person_name + "is" + student_age + "years old")

describe_student('Jack')
describe_student('Jack', '18')

结果:

my name is  Jack
Jackis18years old
my name is  Jack
Jackis18years old

对于年龄不是18岁的学生,就必须明确地传入student_age,如describe_student(‘Herbie’,19)。从上面的例子可以看出,认参数可以简化函数调用
要注意的是,设置认参数时,必选参数在前,认参数在后,否则Python语言的解释器会报错。

认参数很有用,但使用时要牢记一点,认参数必须指向不可变对象,否则会出现错误,如下代码所示。

def test_add(a=[]):
    a.append('END')
    return a

print(test_add([1, 2, 3]))
print(test_add(['a', 'b', 'c']))
print(test_add())
print(test_add())
print(test_add())

结果:

[1, 2, 3, 'END']
['a', 'b', 'c', 'END']
['END']
['END', 'END']
['END', 'END', 'END']

从上述代码可以看出,认参数是空列表[],但是函数test_add()似乎每次都“记住了”上次添加了’END’后的list。这是因为在Python语言中,函数在定义的时候,认参数H的值就被计算出来了,即[]。因为认参数H也是一个变量,它指向对象[]。每次调用函数,如果改变了H的内容,则下次调用时,认参数的内容就变了,不再是函数定义时的[]了。

开发者也可以用None这个不可变对象来解决报错问题,如下代码所示。

def test_add(H=None):
    if H is None:
        H = []
    H.append('END')
    return H

print(test_add())
print(test_add())

结果:

['END']
['END']

3. 不定长参数

在Python语言中,可以在函数参数前面添加“*”号把该参数定义为不定长参数;可以看出,不定长参数的使用使得calc()函数定义和调用都变得简洁,实例如下所示:

def calc(*numbers):
    sum = 0
    for n in numbers:
        sum = sum + n
    return sum

print(calc(1, 2, 3, 4))
print(calc())
num = [1, 2, 3]
print(calc(*num))# 此处传参也用的不定长参数

结果:

10
0
6

4. 关键字参数

关键字实参是传递参数时使用“名称–值”对的方式,在实参中将名称和值关联起来。
关键字实参让开发者无需考虑函数调用中的实参顺序,清楚地指出了函数调用中各个值的用途。

def person(name, age, **kw):
    print('name:', name, 'age:', age, 'other:', kw)

extra = {'city': 'Beijing', 'job': 'Engineer'}
person('Jack', 24, **extra)

结果:

name: Jack age: 24 other: {'city': 'Beijing', 'job': 'Engineer'}

对于关键字参数函数调用者可以传入任意不受限制的关键字参数。至于到底传入了哪些,就需要在函数内部通过kw检查。
以person()函数为例,我们希望检查是否有city和job参数:

def person(name, age, **kw):
    if 'city' in kw:
        # 有city参数
        pass
    if 'job' in kw:
        # 有job参数
        pass
    print('name:', name, 'age:', age, 'other:', kw)

但是调用者仍可以传入不受限制的关键字参数
person(‘Jack’, 24, city=‘Beijing’, addr=‘Chaoyang’, zipcode=123456)

5. 命名关键字参数

如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收city和job作为关键字参数。这种方式定义的函数如下:

def person(name, age, *, city, job):
    print(name, age, city, job)

关键字参数**kw不同,命名关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数
调用方式如下:

person('Jack', 24, city='Beijing', job='Engineer')

如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了:

def person(name, age, *args, city, job):
    print(name, age, args, city, job)

命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错:

person('Jack', 24, 'Beijing', 'Engineer')

结果:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: person() takes 2 positional arguments but 4 were given

由于调用时缺少参数名city和job,Python解释器把这4个参数均视为位置参数,但person()函数仅接受2个位置参数。

命名关键字参数可以有缺省值,从而简化调用

def person(name, age, *, city='Beijing', job):
    print(name, age, city, job)

person('Jack', 24, job='Engineer')

使用命名关键字参数时,要特别注意,如果没有可变参数,就必须加一个作为特殊分隔符。如果缺少,Python解释器将无法识别位置参数和命名关键字参数

def person(name, age, city, job):
    # 缺少 *,city和job被视为位置参数
    pass

6. 参数组合

在Python语言中定义函数,开发者可以组合使用这些参数(必选参数、认参数、可变参数、关键字参数和命名关键字参数)。注意参数定义是有顺序的。定义的顺序必须是:必选参数、认参数、可变参数、命名关键字参数关键字参数

比如要定义一个函数,包含上述若干种参数,如下代码所示。

def func(a, b, c=0, *args, **kw):
    print('a=', a, 'b=', b, 'c=', c, 'args=', args, 'kw=', kw)


print(func(1, 2))  
# 输出结果:a= 1 b= 2 c= 0 args= () kw= {}
print(func(1, 2, 3))  
# 输出结果:a= 1 b= 2 c= 3 args= () kw= {}
print(func(1, 2, 3, 'a', 'b'))  
# 输出结果:a= 1 b= 2 c= 3 args= ('a', 'b') kw= {}
print(func(1, 2, 3, 'a', 'b', x=4))  
# 输出结果:a= 1 b= 2 c= 3 args= ('a', 'b') kw= {'x': 4}
args = (1, 2, 3, 4)
kw = {'x': 5}
print(func(*args, **kw))  
# 输出结果:a= 1 b= 2 c= 3 args= (4,) kw= {'x': 5}

大概也许可能在定义函数的时候可变参数后面也可以定义必选参数,但是传参的时候必须以关键字参数传入

四、函数式编程

函数式编程中的“函数”不是指计算机中的函数,而是指数学中的函数,即自变量的映射。
函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数

1. 高阶函数

接受函数为参数,或者把函数作为结果返回的函数称为高阶函数

2. 匿名函数

所谓匿名函数,即不再使用def语句这样标准形式定义的函数。Python语言经常使用lambda来创建匿名函数。lambda 只是一个表达式,函数体比def定义的函数体要简捷。lambda函数的语法如下所示。

lambda [arg1[,arg2],....argn]]:expression

lambda函数拥有自己的名字空间,且不能访问自有参数列表之外或全局名字空间里的参数
对于复杂函数或者函数体体量大的函数,最好不要用lambda函数,会增加代码的阅读难度,使代码晦涩难懂。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐