一、函数的定义
1.1函数的格式
定义函数的格式如下:
def 函数名():
代码
例如:
# 定义一个函数,能够完成打印信息的功能
def printInfo():
print '------------------------------------'
print ' 人生苦短,我用Python'
print '------------------------------------'
定义了函数之后,就相当于有了一个具有某些功能的代码,想要让这些代码能够执行,需要调用它。调用函数很简单的,通过 函数名() 即可完成调用。
1.2函数的参数
定义带有参数的函数示例如下:
def mul2(a, b):
c = a*b
print c
mul2(7, 9) #调用带有参数的函数时,需要在小括号中传递数据
另外,关于函数参数,有两个具体的概念:形参和实参
(1)定义时小括号中的参数,用来接收参数用的,称为 “形参”
(2)调用时小括号中的参数,用来传递给函数用的,称为 “实参”
其中,形参可以分别是缺省参数,不定长参数; 实参可以分为位置参数和关键字参数。
1.实参的位置参数
函数调用时,按形参的位置,从左往右,一一匹配传递参数。位置参数必须一一对应,缺一不可。
def mul2(a, b):
c = a*b
print c
mul2(7, 9) #7,9这两个实参的传递方法是位置参数
2.实参的关键字参数
函数调用时,通过 形参=值 方式为函数形参传值,不用按照位置为函数形参传值,这种方式叫关键字参数。
def mul2(a, b):
c = a*b
print c
mul2(b=9, a=7) #7,9这两个实参的传递方法是关键字参数,传入顺序可与形参定义顺序不同
注意:
(1)关键字参数必须在位置参数的右边,且对同一个形参不能重复传值。
(2)如果位置参数和关键字参数同时混合使用,那么顺序上必须先传位置参数,再传关键字参数。
3.形参的缺省参数
形参设定默认值,称为缺省参数,也叫默认参数
元组型不定长参数:形参变量名前面加上一个*,这个参数则为元组型不定长参数。元组型可变形参必须在形参列表的最后边
# *args 不定长参数,可以接收0~多个实参
# 把实参的1,2,3, 包装成元组(1, 2, 3)再传递, 等价于args = (1, 2, 3)
def func(*args):
# 函数内部使用,无需加*
print(args, type(args))
# 函数调用
func(1, 2, 3)
-------------------------以下是函数的输入结果----------------------------
(1, 2, 3) <class 'tuple'>
字典型不定长参数:定义参数时需要在形参名前添加**,则为字典型不定长参数。字典型可变形参必须在形参列表的最后边
# 把实参包装成 {'city': 'sz', 'age': 18}给kwargs传递
# kwargs = {'city': 'sz', 'age': 18}
def func(name, **kwargs):
# 同时存在形参name, name不会被包装到字典中
print(name)
print(kwargs) # 函数内部使用,无需加*
# 实参的写法: 变量=数据,变量=数据
func(name='mike', city='sz', age=18)
-------------------------以下是函数的输入结果----------------------------
mike {'city': 'sz', 'age': 18}
函数参数的关键要点总结:
(1)缺省参数需要在非缺省参数之后 。
(2)关键字参数需要在位置参数之后 。
(3)所有传递的关键字参数必须有对应参数,并且顺序不重要。
(4)参数只能赋值一次。
(5)缺省参数是可选参数,可以不传。
1.3函数的返回值
“返回值”,就是程序中函数完成一件事情后,最后给调用者的结果。想要在函数中把结果返回给调用者,需要在函数中使用return关键字,示例如下:
def add2num(a, b):
return a+b
注意,当一个函数返回了一个数据,如果想要用这个数据,那么就需要保存。
保存函数的返回值示例如下:
#定义函数
def add2num(a, b):
return a+b
#调用函数,顺便保存函数的返回值
result = add2num(72,17)
#因为result已经保存了add2num的返回值,所以在接下来的场景中,就可以使用了
print (result) #结果等于89
在python中,我们可以返回多个值。方法是在关键字return后面将多个返回值用逗号隔开就好。
1.4引用和引用传参
在python中,值是靠引用来传递来的。 我们可以用id()来判断两个变量是否为同一个值的引用。 我们可以将id值理解为变量在计算机中的内存地址标示。如下实例:
a = 1
b = a
id(a) #
id(b) # 输出:13033816, 注意两个变量的id值相同
a = 2
id(a) # 输出:13033792, 注意a的id值已经变了
id(b) # 输出:13033816, b的id值依旧
下面具体可变数据类型列表的引用:
a = [1, 2]
b = a
id(a) # 输出:139935018544808
id(b) # 输出:139935018544808, 注意两个变量的id值相同
a.append(3)
print(a) # 输出:[1, 2, 3]
id(a) # 输出:139935018544808, 注意a与b始终指向同一个地址
因此,数据类型可变还是不可变的本质是:在改变数据具体的值时,其内存的id地址索引是否改变;变的话当前数据为不可变数据类型,不变的话则为可变数据类型。
Python中函数参数是引用传递(注意不是值传递)。因此,可变类型与不可变类型的变量分别作为函数参数时,产生的影响是不同的。具体来说,对于不可变类型,因变量不能修改,所以函数中的运算不会影响到变量自身;而对于可变类型来说,函数体中的运算有可能会更改传入的参数变量。示例如下:
a = [1,2,3]
def add(arr):
arr += arr
add(a)
print(a)
上述代码输出的结果为[1,2,3,1,2,3]。即,传入的实参是可变数据类型列表,且通过函数改变了原本的值。再看下面的不改变原本值的例子:
a = [1,2,3]
def add(arr):
arr = arr + arr
add(a)
print(a)
上述代码输出的结果为[1,2,3]。即,传入的实参是可变数据类型列表,但是没有通过函数改变了原本的值。主要原因是 ‘=’ 和 ‘+=’ 的区别: ‘=’ 的使用会直接创建一个新的变量,而 ‘+=’ 则是直接对原变量进行操作。
1.5函数的嵌套调用
一个函数里面又调用了另外一个函数,这就是所谓的函数嵌套调用。示例如下:
def testB():
print('---- testB start----')
print('这里是testB函数执行的代码... ')
print('---- testB end----')
def testA():
print('---- testA start----')
testB() #在函数A中,调用函数B
print('---- testA end----')
testA() #执行函数B
上述代码块的执行结果如下:
---- testA start----
---- testB start----
这里是testB函数执行的代码...
---- testB end----
---- testA end----
二,函数中的变量
2.1局部变量
(1)局部变量,就是在函数内部定义的变量
(2)不同的函数,可以定义相同的名字的局部变量,而且,这些同名局部变量在各自的函数中使用,相互间不会产生影响或冲突
(3)局部变量的作用是为了实现在函数中临时保存数据需要,而定义的变量。
例如,以下函数中的变量a,b,c就是局部变量。
def mul2():
a = 7
b = 9
c = a*b
print c
2.2全局变量
如果一个变量,既能在一个函数中使用,也能在其他的函数中使用,这样的变量就是全局变量。示例如下:
a = 10 #全局变量
def test1():
a = 30
print('test1中的变量a的值:', a)
a = 70
print('test1中的变量a修改后的值:', a)
def test2():
print('test2中的变量a的值:', a)
如上述代码所示,第一行定义在函数外面的a是一个全局变量。
注意,上述代码出现了全局变量和局部变量名字相同问题,即a在函数test1中是局部变量。
此时,代码的运行结果如下:
即,如果全局变量的名字和局部变量的名字相同,那么函数中使用的是局部变量的,记忆小技巧:强龙不压地头蛇。
此外,既然全局变量能够在所有的函数中进行使用,那么可否进行修改呢?当然可以,我们可以使用关键字global在函数中指定修改全局变量,如下所示:
a = 10 #全局变量
def test1():
global a
print('test1中的变量a的值:', a)
a = 70
print('test1中的变量a修改后的值:', a)
def test2():
print('test2中的变量a的值:', a)
上述代码的运行结果如下:
2.3 可变/不可变全局变量
对于不可变的数据类型来说(数字,字符串,元组),如果不使用global,就尝试在函数中修改全局变量会引起报错,如下实例:
a = 1 #定义全局变量
def f():
a += 1
print a
f() #执行函数
-----------------------以下为报错信息-----------------------
在函数中不使用global声明全局变量时,不能修改全局变量的本质是不能修改全局变量的指向,即不能将全局变量指向新的数据。
对于不可变类型的全局变量来说,因其指向的数据不能修改,所以不使用global时无法修改全局变量。
对于可变类型的全局变量来说(列表,字典,集合),因其指向的数据可以修改,所以不使用global时也可修改全局变量。如下示例:
lst = [1,]
def f2():
lst.append(7)
print (lst)
f2()
-----------------------以下为函数f2()执行结果-----------------------
[1, 7]
三、高级函数用法
3.1递归函数
通过前面的学习知道一个函数可以调用其他函数。 如果一个函数在内部不调用其它的函数,而是调用自己本身的话,这个函数就是递归函数。
举个例子,我们来计算阶乘 n! = 1 * 2 * 3 * … * n
看阶乘的规律:
1! = 1
2! = 2 × 1 = 2 × 1!
3! = 3 × 2 × 1 = 3 × 2!
4! = 4 × 3 × 2 × 1 = 4 × 3!
…
n! = n × (n-1)!
我们可以把阶乘定义为某种函数,让它在执行的时候调用它本身,如下述代码所示:
def cal_factorial(num):
if num >= 1:
result = num * cal_factorial(num-1)
else:
result = 1
return result
当num=3时,上述代码的执行逻辑如下图所示:
除了上述完成的递归实现,其实阶乘还可以使用循环来实现,如下代码所示:
def cal_factorial(num):
i = 1
result = 1
while i <= num:
result *= i
i += 1
return result
3.2匿名函数
匿名函数是用关键词lambda创建的小型函数。这种函数得名源于定义时,省略了用def声明函数的标准步骤。
lambda函数的语法只包含一个语句,如下
lambda [arg1 [,arg2,.....argn]]:expression
下面使用匿名函数实现两数相加的操作:
cal_sum = lambda arg1, arg2: arg1 + arg2
#调用sum函数
print("Value of total : ", cal_sum( 10, 20 ) )
print("Value of total : ", cal_sum( 20, 20 ) )
以上实例输出结果:
需要注意的是,Lambda函数能接收任何数量的参数但只能返回一个表达式的值。而且,匿名函数不能直接调用print,因为lambda需要一个表达式。
匿名函数大多数的应用场合是作为参数传递,因为其小巧灵活的编写方式。举个例子:
#定义函数
def fun(a, b, opt):
print "a =", a
print "b =", b
print "result =", opt(a, b)
#执行函数
fun(1, 2, lambda x,y:x+y)
----------------------------------以下是函数的执行返回结果----------------------------------
a = 1
b = 2
result = 3