Python3从入门到实战
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

3.1 定义函数、调用函数、参数传递

3.1.1 定义函数和调用函数

Python通过关键字def定义函数(给1个程序块起1个名字)。例如,下列代码定义了1个叫作hello的函数:

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

该函数的程序块中包含了2条函数print()调用语句。此后,就可以通过函数名hello调用这个函数hello()。例如,下面代码调用了2次函数hello():

hello()
hello()

输出结果:

hello,
world
hello,
world

假如希望上述的函数hello()可以根据不同的输入,如不同的人名,输出针对特定人名的输出信息,为此可以进行如下修改:

# hello函数包含一个叫作name的参数
def hello(name):
    print("hello, ")
    print(name)

函数的函数名后的圆括号里说明了一个参数name,函数体的print(name)就输出这个name参数的值。调用该函数时,就可以向这个name传递不同的对象。例如:

hello("Li Ping")         #将"Li Ping"传递给被调用函数hello()的name参数
hello("Zhang Wei")

例如,语句hello("Li Ping")调用函数hello(),并将“Li Ping”传递给函数hello()的name参数,即name引用的就是这个字符串“Li Ping”。函数hello()的print(name)将输出这个name引用的“Li Ping”。

因此,定义函数的语法格式如下:

def 函数名(参数1,参数2, …):
    函数体程序块

def 关键字之后是函数名,函数名后是一对圆括号,圆括号中可以包含一些称为“参数”的变量,圆括号中包含的参数变量称为函数的“参数列表”(参数列表可以是空的,即不包含任何参数),圆括号的后面是一个冒号:。从def关键字一直到该行结尾的冒号构成了函数头。函数头下面的是一组语句构成的语句块,称为函数体

因此,函数由函数头函数体构成。函数头说明了函数的名字和参数有哪些,而函数体是具体的函数代码(语句)。函数的参数说明了函数的调用者可以向该函数传递什么样的数据,这些参数之间用逗号隔开。函数也可以没有任何参数,如同上面的函数hello()一样,但没有参数的函数总是执行同样的数据处理,不能根据实际情况执行不同的数据处理。

调用函数就是通过传递一些实际数据给函数后圆括号中的“参数”,从而调用执行这个函数名引用的函数体语句,即执行这个函数中的代码语句。

s=input("请输入一个人名:")
hello(s)

其中,第2句调用了函数hello(),并传递从键盘输入的字符串s给函数hello()的参数name。

输出结果:

请输入一个人名:李平
hello,
李平

将键盘输入的字符串“李平”传给了函数hello()的name参数,函数hello(name)的“print(name)”语句将输出参数name指向的对象,也就是s指向的字符串对象“李平”。

如果调用函数hello()时没有提供参数,例如:

hello()

则会报错,错误提示是“缺少一个需要的位置参数:‘name' ”:

---------------------------------------------------------------------------
TypeError                            Traceback(most recent call last)
<ipython-input-5-a75d7781aaeb> in <module>()
----> 1 hello()

TypeError: hello()missing 1 required positional argument: 'name'

如果调用函数hello()时提供了2个以上的参数,则同样会报错,例如:

hello('hello', 'world')
---------------------------------------------------------------------------
TypeError                            Traceback(most recent call last)
<ipython-input-2-fac44797b17f> in <module>()
----> 1 hello('hello', 'world')

TypeError: hello()takes 1 positional argument but 2 were given

3.1.2 参数传递

定义函数时,圆括号的参数称为“形式参数(parameters)”,又称形参,而调用函数时传递的数据称为“实际参数(arguments)”,又称实参。调用函数时,将实际参数传递给形式参数的过程称为“参数传递”。例如,上面的代码将实际参数s传给形式参数name,实际上这是一个实际参数给形式参数赋值的过程,参数传递相当于执行了:

name=s

因此,参数传递就是给形式参数赋值,和普通的变量赋值没有任何区别。例如:

def fun(x):
    print("x的id:", id(x))
    x+=3
    print("x的id:", id(x))
    print("x的值:", x)

a=2
print("a的id:", id(a))
fun(a)
print("a的值:", a)
print("a的id:", id(a))

输出:

a的id: 1717202432
x的id: 1717202432
x的id: 1717202528
x的值: 5
a的值: 2
a的id: 1717202432

当调用fun(a)时,将a赋值给形参x,这时实参和形参是同一个对象的引用(如图3-1(a)所示)。但当执行x+=3时,也就是执行x=x+3时,为x+3创建了一个新对象,形参x引用了这个新的对象(如图3-1(b)所示),其后一句的print("x的id", id(x))打印的就是新对象的id。接着的print("x的值:", x)就是打印的这个x指向的新对象的值5。当这个函数结束后,print("a的值:", a)打印的仍然是a指向的原来的对象(值2)。

图3-1 形参引用实参、给形参赋值

再看以下例子:

def fun_2(x):
    print("x的id:", id(x))
    x[0]=3.14
    print("x的id:", id(x))
    print("x的值:", x)

a=[2,3]
print("a的id:", id(a))
fun_2(a)
print("a的值:", a)
print("a的id:", id(a))

输出:

a的id: 2826240248712
x的id: 2826240248712
x的id: 2826240248712
x的值: [3.14, 3]
a的值: [3.14, 3]
a的id: 2826240248712

因为在函数fun_2()里只修改了x指向的可变对象,而没有修改x本身(没有给x赋值),所以x始终指向的都是实参a指向的对象。

3.1.3 return语句

一个函数可以通过关键字return返回函数的处理结果。例如,编写一个计算两个整数之间的所有整数之和的函数:

def sum(m, n):
    if m>n:               #保证m不大于n
      m, n=n, m          #交换m, n
    sum=0
    i=m
    while i<=n:
      sum +=i
      i +=1
    return sum

上述函数中的“return sum”通过关键字return返回m和n之间整数之和sum。调用这个函数时,需要传递两个整数给它。

ret=sum(3,10)
print(ret)

输出:

52

1.最大公约数

两个正整数的最大公约数就是能被二者整除的最大正整数。给定两个正整数m和n,假设用符号GCD(m, n)表示二者的最大公约数,它们的最大公约数可以用下列式子来计算:

其中,%表示取余数运算。因此,可以用如下的迭代的方法求两个正整数(72和27)的最大公约数:

根据上述过程,可以定义一个求最大公约数的函数。例如:

def GCD(m, n):
    while n!=0:
      m, n=n, m%n
    return m

这个函数GCD()用于计算两个形参表示的整数的最大公约数,通过关键字return返回结果。有了这个函数,其他的程序代码就可以多次调用这个函数来计算两个整数的最大公约数。例如:

m=72
n=27

gcd=GCD(m, n)
print('gcd(72,27):=', gcd)

m=24
n=36
gcd=GCD(m, n)
print('gcd(24,36):=', gcd)

输出:

gcd(72,27):=9
gcd(24,36):=12

2.返回多个值

关键字return不仅可以返回一个值,还可以同时返回多个值,这些值将会被包裹在一个tuple对象中返回。例如,下列的代码可以返回一个list对象中所有数的最大值和最小值。

def min_max(arr):
    m=arr[0]
    M=arr[0]
    for e in arr:
      if e<m:
        m=e
      if e>M:
      M=e
    return m, M;
ret=min_max([36,2,19,76,91,47])
print(ret)
print(ret[0])
print(ret[1])

输出:

(2, 91)
2
91

3.1.4 文档字符串

定义函数时,可以在函数头后面添加由三个引号(三个单引号或三个双引号)括起来的文档字符串(docstring),用于说明这个函数的功能。docstring会作为函数对象的一个属性“__doc__”被使用。例如,可以通过函数print()输出这个属性:

def hi():
    '''
    hi,
    这是一个doc string的例子
    '''
    print("hi, world")

使用这个函数:

hi()                 #调用hi函数
print(hi.__doc__)    #打印hi函数的 __doc__属性

输出:

hi, world
hi,
    这是一个doc string的例子

文档字符串可以有助于他人理解这个函数的功能,Python的每个对象都有一个“__doc__”属性,除直接输出一个对象的“__doc__”属性外,Python的内置函数也同样可以输出文档字符串的内容。许多文档自动化工具可以从程序代码中提取文档字符串,用于进行处理、打印、显示文档等操作。号,圆括号里可以包含参数(圆括号里的所有参数称为函数的参数列表),圆括号后是一个冒号。函数体里的语句必须具有一致的缩进。

总结

● 函数定义包含函数头和函数体。函数头以关键字def开头,后面跟函数名,函数名后是圆括

● 调用函数的方法:函数名后用圆括号传递实参给形参。

● 实参传递给形参的参数就是普通的变量赋值,也就说,形参和实参引用的是同一个对象(当实参本身是对象时,形参就是这个实参对象的引用)。

● 函数中对形参的赋值使形参指向了另外的对象,但对形参引用的可变对象的修改不会改变形参本身。

● 函数通过关键字return返回计算的结果。

● 函数头后面可以通过3个引号表示的字符串给函数添加一个文档串属性__doc__。这个属性是对函数的说明,可以帮助使用者通过查询这个文档串了解这个函数的功能和使用方法。