什么是函数?函数(function)是用于完成特定任务的程序代码的自包含单元。在面向对象编程的类中,函数通常被称作方法。
第一、函数的使用可以重用代码,省去重复性代码的编写,提高代码的重复利用率。如果程序中需要多次使用某种特定的功能,那么只需要编写一个合适的函数就可以了。程序可以在任何需要的地方调用该函数,并且同一个函数可以在不同的程序中调用,就像我们经常使用的print()和input()函数一样。
第二、函数能封装内部实现,保护内部数据,实现对用户的透明。很多时候,我们把函数看做“黑盒子”,即对应一定的输入会产生特定的结果或返回某个对象。往往函数的使用者并不是函数的编写者,函数的使用者对黑盒子的内部行为并不需要考虑,可以把精力投入到自身业务逻辑的设计而不是函数的实现细节。只有函数的设计者或者说编写者,才需要考虑函数内部实现的细节,如何暴露对外的接口,返回什么样的数据,也就是API的设计
def 函数名(参数):
# 内部代码
return 表达式
在定义函数的过程中,需要注意以下几点:
1、函数代码块以def关键词开头,一个空格之后接函数标识符名称和圆括号(),再接个冒号。
2、任何传入的参数必须放在圆括号中间。
3、函数的第一行语句后可以选择性地使用文档字符串—用于存放函数说明。
4、函数内容以冒号起始,并且缩进。
5、使用return结束函数。默认返回None。
6、return语句依然在函数体内部,不能回退缩进。直到函数的所有代码写完,才回退缩进,表示函数体结束。
函数编写出来就是给人调用的。要调用一个函数,必须使用函数名后跟圆括号的方式才能调用函数。调用的同时要根据函数的定义体,提供相应个数和类型的参数,每个参数之间用逗号分隔。Python由于动态语言的特点,在做语法和词法分析检查的时候,并不会对参数类型进行检查,但在执行过程中,如果参数类型不符合函数内部运行机制的话,会弹出相应的错误
return语句用于表示函数执行到此结束,并且返回return后面的对象。有时候,函数不需要返回任何值,此时可以不需要return语句,它在后台默认给你返回个None,并且不给任何提示。但是更多的时候我们还是需要return一些东西。一旦函数执行过程遇到return语句,那么之后函数体内的所有代码都会被忽略,直接跳出函数体
1、不写return语句,它在后台默认给你返回个None,并且不给任何提示
2、什么都不返回,仅仅return,相当于不写return语句,
3、数字/字符串/任意数据类型: return 'hello'
4、一个表达式:return 1+2
5、一个判断语句:return 100 > 99
6、一个变量:return a
7、一个函数调用:return func()
8、甚至是返回自己!:return self
多个返回值,以逗号分隔:return a, 1+2, "hello"
我们在调用函数的时候,可以将函数的返回值保存在变量中。
def func():
pass
return "something"
result = func()
而对于同时返回多个值的函数,需要相应个数的变量来接收,变量之间以逗号分隔:
def func():
return 1, [2, 3], "haha"
a, b, c = func()
x, y, z = 1, 2, 3
def add(a, b, c):
return a+b+c
add(x, y, x) # 使用变量,传递参数
a,b,c叫做形式参数,简称形参。而x,y,z和4,5,6叫做实际参数,简称实参,也就是实际要传递的值。而我们通常讨论的参数,指的都是形参。
python参数类型有:位置参数,还可以使用默认参数、动态参数和关键字参数,这些都是形参的种类。
也叫必传参数,顺序参数,是最重要的,也是必须在调用函数时明确提供的参数!位置参数必须按先后顺序,一一对应,个数不多不少的传递!
注意: Python在做函数参数传递的时候不会对数据类型进行检查,理论上你传什么类型都可以
def add(a, b, c):
return a+b+c
result = add(1, 2, 3)
也叫必传参数,顺序参数,是最重要的,也是必须在调用函数时明确提供的参数!位置参数必须按先后顺序,一一对应,个数不多不少的传递
注意: Python在做函数参数传递的时候不会对数据类型进行检查,理论上你传什么类型都可以,但在执行过程中,如果参数类型不符合函数内部运行机制的话,会弹出相应的错误
但是 result = add('ab',2,3) 如果你传递了一个字符串和两个数字,结果是弹出异常,因为字符串无法和数字相加。这就是Python的弱数据类型和动态语言的特点
在函数定义时,如果给某个参数提供一个默认值,这个参数就变成了默认参数,不再是位置参数了。在调用函数的时候,我们可以给默认参数传递一个自定义的值,也可以使用默认值
def power(x, n = 2):
return x**n
ret1 = power(10) # 使用默认的参数值n=2,实际计算 10**2的值
ret2 = power(10, 4) # 将4传给n,实际计算10**4的值
使用默认值参数的注意事项:
1、默认参数必须在位置参数后面!
# 这是一个错误的例子
def power(n = 2,x):
return x**n
2、当有多个默认参数的时候,通常将更常用的放在前面,变化较少的放后面。
def student(name, sex, age, classroom="101", tel="88880000", address="..."):
pass
3、在调用函数的时候,尽量给实际参数提供默认参数名。
def student(name, sex, age, classroom="101", tel="88880000", address="..."):
pass
student('jack','male',17) # 其它全部使用默认值
student('tom','male',18,'102','666666','beijing') # 全部指定默认参数的值
student('mary','female',18,'102',tel='666666') # 挑着来
student('mary','female',18,tel='666666','beijing') # 这是错误的参数传递方式
student("mary","female",18,tel="666666",address="beijing")
因为一切没有提供参数名的实际参数,都会当做位置参数按顺序从参数列表的左边开头往右匹配!
4、使用参数名传递参数
通常我们在调用函数时,位置参数都是按顺序先后传入,而且必须在默认参数前面。但如果在位置参数传递时,给实参指定位置参数的参数名,那么位置参数也可以不按顺序调用,例如:
def student(name, age, classroom, tel, address="..."):
pass
student(classroom=101, name="Jack", tel=66666666, age=20)
注意指定的参数名必须和位置参数的名字一样。
顾名思义,动态参数就是传入的参数的个数是动态的,可以是1个、2个到任意个,还可以是0个。在不需要的时候,你完全可以忽略动态函数,不用给它传递任何值
Python的动态参数有两种,分别是*args和**kwargs,这里面的关键是一个和两个星号的区别,而不是args和kwargs在名字上的区别,实际上你可以使用*any或**whatever的方式。但就如self一样,默认大家都使用*args和**kwargs。
注意:动态参数,必须放在所有的位置参数和默认参数后面
def func(name, age, sex='male', *args, **kwargs):
pass
*args
一个星号表示接收任意个数的参数。调用时,会将实际参数打包成一个元组传入形式参数。如果参数是个列表,会将整个列表当做一个参数传入。
如果我们想把列表中的每一个元素分别当作参数传给函数时,传参时在列表前面加一个*,就可以达到目的,任何序列类型数据对象,比如字符串、元组都可以通过这种方式将内部元素逐一作为参数,传递给函数。而字典,则会将所有的key逐一传递进去
**args
两个星表示接受键值对的动态参数,数量任意。调用的时候会将实际参数打包成字典,如果我们希望们字典内的键值对能够像上面一样被逐一传入。使用两个星号**,两个星号能将字典内部的键值对逐一传入**kwargs
当*args
和**kwargs
组合起来使用,理论上能接受任何形式和任意数量的参数,在很多代码中我们都能见到这种定义方式。需要注意的是,*args
必须出现在**kwargs
之前。
加* 和 不加*的对比
def func(*args, **kwargs):
for arg in args:
print(type(arg))
print(arg)
for kwg in kwargs:
print(type(kwg))
print(kwg, kwargs[kwg])
lis = [1, 2, 3]
dic = {
'k1': 'v1',
'k2': 'v2'
}
func(lis, dic)
>>>(不加*)
[1, 2, 3]
{'k1': 'v1', 'k2': 'v2'}
func(*lis,**dic)
>>>(加*)
1
2
3
k1 v1
k2 v2
作用域指的是变量的有效范围。变量并不是在哪个位置都可以访问的,访问权限取决于这个变量是在哪里赋值的,也就是在哪个作用域内的。
函数就是内部代码可以访问外部变量,而外部代码通常无法访问内部变量
变量的作用域决定了程序的哪一部分可以访问哪个特定的变量名称。Python的作用域一共有4层,分别是:
x = int(2.9) # 内建作用域,查找int函数
global_var = 0 # 全局作用域
def outer():
out_var = 1 # 闭包函数外的函数中
def inner():
inner_var = 2 # 局部作用域
如果出现本身作用域没有定义的变量,那该如何寻找呢?
Python以L –> E –> G –>B的规则查找变量,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,最后去内建中找。如果这样还找不到,那就提示变量不存在的错误。
定义在函数内部的变量拥有一个局部作用域,被叫做局部变量,定义在函数外的拥有全局作用域的变量,被称为全局变量
所谓的局部变量是相对的。局部变量也有可能是更小范围内的变量的外部变量。
局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中。
a = 1 # 全局变量
def func():
b = 2 # 局部变量
print(a) # 可访问全局变量a,无法访问它内部的c
def inner():
c = 3 # 更局部的变量
print(a) # 可以访问全局变量a
print(b) # b对于inner函数来说,就是外部变量
print(c)
total = 0 # total是一个全局变量
def plus( arg1, arg2 ):
total = arg1 + arg2 # total在这里是局部变量.
print("函数内局部变量total= ", total)
print("函数内的total的内存地址是: ", id(total))
return total
plus(10, 20)
print("函数外部全局变量total= ", total)
print("函数外的total的内存地址是: ", id(total))
>>>
函数内局部变量total= 30
函数内的total的内存地址是: 4540117584
函数外部全局变量total= 0
函数外的total的内存地址是: 4540116624
很明显,函数plus内部通过total = arg1 + arg2语句,新建了一个局部变量total,它和外面的全局变量total是两码事。而如果我们,想要在函数内部修改外面的全局变量total呢?使用global关键字!
global:指定当前变量使用外部的全局变量
total = 0 # total是一个全局变量
def plus( arg1, arg2 ):
global total # 使用global关键字申明此处的total引用外部的total
total = arg1 + arg2
print("函数内局部变量total= ", total)
print("函数内的total的内存地址是: ", id(total))
return total
plus(10, 20)
print("函数外部全局变量total= ", total)
print("函数外的total的内存地址是: ", id(total))
>>>
函数内局部变量total= 30
函数内的total的内存地址是: 503494624
函数外部全局变量total= 30
函数外的total的内存地址是: 503494624
a = 1
print("函数outer调用之前全局变量a的内存地址A: ", id(a),a)
def outer():
a = 2
print("函数outer调用之时闭包外部的变量a的内存地址B: ", id(a),a)
def inner():
global a # 注意这行
a = 3
print("函数inner调用之后闭包内部变量a的内存地址C: ", id(a),a)
inner()
print("函数inner调用之后,闭包外部的变量a的内存地址D: ", id(a),a)
outer()
print("函数outer执行完毕,全局变量a的内存地址E: ", id(a),a)
>>>
函数outer调用之前全局变量a的内存地址A: 4494626480 1
函数outer调用之时闭包外部的变量a的内存地址B: 4494626512 2
函数inner调用之后闭包内部变量a的内存地址C: 4494626544 3
函数inner调用之后,闭包外部的变量a的内存地址D: 4494626512 2
函数outer执行完毕,全局变量a的内存地址E: 4494626544 3
---------------------------------------------
a = 1
print("函数outer调用之前全局变量a的内存地址A: ", id(a),a)
def outer():
a = 2
print("函数outer调用之时闭包外部的变量a的内存地址B: ", id(a),a)
def inner():
nonlocal a # 注意这行
a = 3
print("函数inner调用之后闭包内部变量a的内存地址C: ", id(a),a)
inner()
print("函数inner调用之后,闭包外部的变量a的内存地址D: ", id(a),a)
outer()
print("函数outer执行完毕,全局变量a的内存地址E: ", id(a),a)
>>>
函数outer调用之前全局变量a的内存地址A: 4525268656 1
函数outer调用之时闭包外部的变量a的内存地址B: 4525268688 2
函数inner调用之后闭包内部变量a的内存地址C: 4525268720 3
函数inner调用之后,闭包外部的变量a的内存地址D: 4525268720 3
函数outer执行完毕,全局变量a的内存地址E: 4525268656 1
Python中,在函数中为一个变量赋值时,当在函数中给一个变量名赋值是,Python总是创建或改变本地作用域的变量名,除非它已经在那个函数中被声明为全局变量.
若想要在函数中修改全局变量x,而不是在函数中新建一个变量,此时便要用到关键字global
nonlocal
关键字可以在一个嵌套的函数中修改嵌套作用域中的变量,会修改 1 处的count值
Python函数的作用域取决于其函数代码块在整体代码中的位置,而不是调用时机的位置。调用f1的时候,会去f1函数的定义体查找,对于f1函数,它的外部是name ='jack'
,而不是name = 'eric'
。
a = 10
def test():
a += 1
print(a)
test()
我会告诉你,这段代码有语法错误吗?a += 1相当于a = a + 1,按照赋值运算符的规则是先计算右边的a+1。但是,Python的规则是,如果在函数内部要修改一个变量,那么这个变量需要是内部变量,除非你用global声明了它是外部变量。很明显,我们没有在函数内部定义变量a,所以会弹出局部变量在未定义之前就引用的错误。
a = 10
def test():
global a
a += 1
print(a)
test()
>>>
11