函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。函数能提高应用的模块性,和代码的重复利用率。
Python 中函数的应用非常广泛,前面讲座中我们已经使用过过多个函数,比如 input() 、print()、range()函数等等,这些都是 Python 的内置函数,可以直接使用。
除了可以直接使用的内置函数外,Python 还支持自定义函数,即将一段有规律的、可重复使用的代码定义成函数,从而达到一次编写、多次调用的目的。
Python函数的一般分类
(1)内置函数。Python语言内置了若干常用的函数,例如abs()、len()等等,在程序中可以直接使用。
(2)标准库函数。Python语言安装程序同时会安装若干标准库,例如math、random等等。通过import语句,可以导入标准库,然后使用其中定义的函数。
(3)第三方库函数。Python社区提供了许多其他高质量的库,如Python图像库等等,需要下载安装。下载安装这些库后,通过import语句,可以导入库,然后使用其中定义的函数。
(4)用户自定义函数。本讲座将讨论函数的定义和调用方法。
自定义函数的简单规则:
函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()和冒号:。
任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。
函数内容(函数体)缩进。
Python 定义函数的语法格式
Python 定义函数使用 def 关键字,一般格式如下:
def 函数名(参数列表):
函数体
[return [返回值]]
各部分参数的含义如下:
函数名:其实就是一个符合 Python 语法的标识符,但不建议读者使用 a、b、c 这类简单的标识符作为函数名,函数名最好能够体现出该函数的功能(如上面的 my_len,即表示我们自定义的 len() 函数)。
参数列表:设置该函数可以接收多少个参数,多个参数之间用逗号( , )分隔。
[return [返回值] ]:整体作为函数的可选参参数,用于设置该函数的返回值。也就是说,一个函数,可以用返回值,也可以没有返回值,是否需要根据实际情况而定。函数中可以有多条return语句,只要执行了一条return语句,程序返回到调用程序。
注意,在创建函数时,即使函数不需要参数,也必须保留一对空的“()”,否则 Python 解释器将提示“invaild syntax”错误。
默认情况下,参数值和参数名称是按函数声明中定义的顺序匹配起来的。
例、#定义一个比较字符串大小的函数
#函数的定义(声明)
def s_max(s1,s2):
if s1>s2:
return s1
else:
return s2
Python函数的调用
python函数的应用一般需要:先定义(声明)、后调用。
定义一个函数只给了函数一个名称,指定了函数里包含的参数,和代码块结构。
这个函数的基本结构完成以后,你可以通过另一个函数调用执行,也可以直接从Python提示符执行。
函数调用的基本语法格式如下
函数名([参数值])
调用函数一方,可以使用变量来接收函数的返回结果。
s1= s_max('li','wang') #函数的调用
print(s1)
参见下图:
函数的定义和调用示例2、计算并返回第n阶调和数(1 + 1/2 + 1/3 + … + 1/n)的函数,输出前n个调和数
def harmonic(n): #自定义函数:计算n阶调和数(1 + 1/2 + 1/3 + … + 1/n)
total = 0.0
for i in range(1, n+1):
total += 1.0 / i
return total
n = int(6) #调用前用定义的函数
for i in range(1, n+1): #输出前n个调和数的值
print(harmonic(i))
参数的作用
函数的参数,增加函数的通用性,针对相同的数据处理逻辑,能够适应更多的数据。
1.在函数内部,把参数当作变量使用,进行需要的数据处理。
2.函数调用时,按照函数定义的参数顺序,把希望在函数内部处理的数据,通过参数传递。
形式参数(简称形参)和实际参数(简称实参)
形参:定义函数时,小括号中的参数,是用来接收参数用的,在函数内部作为变量使用。
实参:调用函数时,小括号中的参数,是用来把数据传递到函数内部用的。
声明函数时声明的形式参数,等同于函数体中的局部变量,在函数体中的任何位置都可以使用。
局部变量和形式参数变量的区别在于,局部变量在函数体中绑定到某个对象;而形式参数变量则绑定到函数调用代码传递的对应实际参数对象。
Python 中,根据实际参数的类型不同,函数参数的传递方式可分为 2 种:值传递和引用(地址)传递:
值传递:参数类型应为不可变类型(字符串、数字、元组);
引用(地址)传递:参数类型应为可变类型(列表,字典);
值传递和引用传递的区别是,函数参数进行值传递后,若形参的值发生改变,不会影响实参的值;而函数参数继续引用传递后,改变形参的值,实参的值也会一同改变。
下面程序演示了函数参数值传递的情况:
def swap(a , b) :
# 下面代码实现a、b变量的值交换
a, b = b, a
print("swap函数里,a的值是", \
a, ";b的值是", b)
a = 6
b = 9
swap(a , b)
print("交换结束后,变量a的值是", \
a , ";变量b的值是", b)
运行结果:
下面程序演示了函数参数引用传递的情况:
def swap(dw):
# 下面代码实现dw的a、b两个元素的值交换
dw['a'], dw['b'] = dw['b'], dw['a']
print("swap函数里,a元素的值是",\
dw['a'], ";b元素的值是", dw['b'])
dw = {'a': 6, 'b': 9}
swap(dw)
print("交换结束后,a元素的值是",\
dw['a'], ";b元素的值是", dw['b'])
运行结果:
在Python中要让函数修改某些数据,则可以通过把这些数据包装成列表、字典等可变对象,然后把列表、字典等可变对象作为参数传入函数,在函数中通过列表、字典的方法修改它们,这样才能改变这些数据。
【Python中函数参数的传递机制补充说明:
不可变参数通过“值”进行传递。像元组、字符串这样的对象是通过对象引用而不是拷贝进行传递的,但是你不可能在原处修改来改变不可变对象,实际效果像是创建了一份拷贝。
可变对象通过"指针"进行传递:像列表和字典这样的对象也是通过对象引用进行传递的,但是由于你可以在原处修改可变对象,类似于C语言使用指针进行传递的情况。
本质上,Python的参数传递都是通过引用来进行传递的,由于不可变对象和可变对象的修改机制不同,造成了不同的结果。这本身不是bug,而是Python的工作方式。
对于不可变对象,如int类型,元组和字符串等,参数名称可能最初共享传递的对象(实质上是指向这些对象的指针),但只是临时的,仅当函数第一次调用的时候如此。只要对参数名重新赋值,这种关系就结束了。因为不可变对象不支持在原处修改。我们可以看出,在函数调用结束时,number变量和num函数参数引用了不同的对象。
对于可变对象,当参数传递项列表和字典这样的可修改对象的时候,就需要注意,对这样的对象的原处修改,可能在函数退出后依然有效,并由此影响到调用者。】
变量的作用域
局部变量、全局变量和类型成员变量
局部变量是在函数内部定义的变量,只能在函数内部使用,临时保存函数内部需要使用的数据,函数执行结束后,函数内部的局部变量,会被系统回收。
不同的函数,可以定义相同的名字的局部变量,但是各用各的不会产生影响。
如果在一个函数中定义的局部变量(或形式参数变量)与全局变量重名,则局部变量(或形式参数变量)优先,即函数中定义的变量是指局部变量(或形式参数变量),而不是全局变量。
全局变量:在一个源代码文件中,在函数和类定义之外声明的变量。
全局变量的作用域为其定义的模块,从定义的位置起,直到文件结束位置。
全局变量是在函数外部定义的变量,(没有定义在某一个函数内),所有函数内部都可以使用这个变量。
通过import语句导入模块,也可以通过全限定名称“模块名.变量名”访问。或者通过from…import语句导入模块中的变量并访问。
例、
#局部变量全局变量的例子
num = 100 #全局变量
def f():
num = 105 #局部变量
print("输出函数内的局部变量的值:",num)
#测试代码
f()
print("输出函数外的全局变量的值:",num)
在函数体中,可以引用全局变量,但如果函数内部的变量名是第一次出现且在赋值语句之前(变量赋值),则解释为定义局部变量(不存在),参见下例:
m = 100
n = 200
def f():
print(m+10) #引用全局变量m
n =n + 10 #注意这句错误
#测试代码
f()