预习:
一 函数的返回值可以以元组的形式返回多个。
补充一个小知识:
判断是否为元组的核心是逗号,即便是没有()包裹起来,它也是元组。明白这一点,就会有恍然大悟的感觉。
例如:
a=1,2 print(a,type(a))
输出:
(1, 2) <class 'tuple'>
所以,下面就比较容易理解了:
def plus(x,y): return x,x+y #看起来是两个返回值,本质上是一个元组,因为有个逗号,所有python会认为x,x+y 就是元组(x,x+y)。下面的结果就是理所当然 print(plus(1,2))
等价于:
def plus(x,y): a=x,x+y #在这里,a就是一个元组。 return a print(plus(1,2))
输出:
(1, 3)
二 函数传参的时候注意可变类型与不可变类型。
函数定义阶段读取的时候,函数变量会被一同读取。默认参数的值在定义阶段就确定了。函数体不被读取。
传参的时候,一定要区分可变类型与不可变类型。两者会有本质的区别。
传的参数是不可变类型时:
name='hello' def func(x): return x.replace('l','g')
result=func(name) print(result) print(name)
输出:
heggo hello
会发现,传入的变量本身并没有变化。
传的参数是可变类型时:
l=[1,2,3,4] def func(x): x.append(5) return x
result=func(l) print(result)
print(l)
输出:
[1, 2, 3, 4, 5] [1, 2, 3, 4, 5]
去上面不可变类型不同的是,可变类型作为参数传入的时候,会被函数所影响,会发生变化。
原因就在于,传参实际上是有这样一个过程。参数传进来的时候,会有一个赋值的过程。
name='hello' def func(x): return x.replace('l','g') result=func(name) # name='hello' # x=name # x='hello' #x,name在这个时候尽管都等于‘hello’,指向‘hello’在内存中的同一个地址。但是因为是不可变类型,所哟x.repalce()之后,x就会指向一个新的内存地址,表现出来x发生了变化。而name本身没有变化 print(result) print(name)
l=[1,2,3,4] def func(x): x.append(5) return x result=func(l) # l=[1,2,3,4] #x=l,实际上变量名x,变量名l,指向内存中同一个空间。列表是可变类型,对其的方法都会使其本身发生变化。 # x=l # x=[1,2,3,4] print(result) print(l)
三 关于 *与**
作用:
*与**在函数定义阶段(形参)起收集的作用,在函数调用阶段(实参)是打散分配的作用。*针对列或者元组格式,**针对是字典格式。
*与**在形参位置:
def func(*args): print(args) func(1,2,3,4) def func(**kwargs): print(kwargs) func(x=1,y=2)
输出:
(1, 2, 3, 4) {'x': 1, 'y': 2}
*与**在形参位置 实参位置:
def func(*args): print(args) a=(1,2,3,4) func(*a) d=dict(x=1,y=2) def func(**kwargs): print(kwargs) func(**d)
输出:
(1, 2, 3, 4) {'x': 1, 'y': 2}
什么时候用呢,请看下面示例:
msg=dict(name='egon',age=88) #msg数据类型是字典 def func(**kwargs): # 函数定义阶段(形参) print('{}老师已经{}岁了'.format(kwargs['name'],kwargs['age'])) func(**msg) #函数调用阶段(实参) def fun(kwargs): print('{}老师已经{}岁了'.format(kwargs['name'], kwargs['age'])) #传的参数数据类型是字典是可以的 fun(msg)
输出:
egon老师已经88岁了 egon老师已经88岁了
func()定义和调用阶段都使用了*,fun()函数两处都没有用到,但得到了同样的效果。所以型号只在定义函数(允许使用不定数目的参数)或者调用(分割字典或者序列)时才有用。
四 作用域(命名空间):namespace
每个函数调用都会创建一个新的作用域。
python的函数是可以嵌套的,也就是可以将一个函数放在另一个函数内部。
举个例子:
def func1(): def func2(): print('hello world') func2() func1()
输出:
hello world
由函数是可以嵌套的,可以引出一个很重要的概念——闭包函数。简单的来说,闭包函数是由两层函数构成,调用外层函数返回内层函数的地址,此时的内层函数并没有被调用。 之后再对内层函数传参加括号调用时,重点到了,内层函数除了自己本身的作用域可以访问,还可以访问它的定义所在的作用域。换句话说,内层函数是带着定义它的环境和(相关的自己局部变量)。
函数存储子封闭作用域的行为叫做闭包。
闭包函数是针对内层函数而说的,内层函数先天是是带着外层函数的作用域和它自己本身的作用域。
def func1(x): def func2(y): return x+y return func2 var=func1(2) res=var(3) print(res)
输出:5
想当然的直接调用func2,是会出现这种情况。
def func1(x): def func2(y): return x+y return func2 res=func2(3) #这一行报错,因为向上找找不到func2这个函数,找不到定义它的语句。报错。 print(res)
五 函数式编程
把函数作为参数传入,这样的函数就称为高阶函数。而函数式编程就是指这种高度抽象的编程范式。
在python2 与 python3 中会有不同:
map():映射。
在python2中:
>>> map(lambda x:x**2,[1,2,3,4,5]) [1, 4, 9, 16, 25]
在python3中:
>>> map(lambda x:x**2,[1,2,3,4,5]) <map object at 0x000001D455A48A90>
>>> list(map(lambda x:x**2,[1,2,3,4,5])) [1, 4, 9, 16, 25]
filte():过滤
在python2中:
>>> map(lambda x:x**2,[1,2,3,4,5]) [1, 4, 9, 16, 25]
在python3中
>>> filter(lambda x:x%2==1,[1,2,3,4,5]) <filter object at 0x000001D455A48A58> >>> list(filter(lambda x:x%2==1,[1,2,3,4,5])) [1, 3, 5]
reduce()
在python2中:
>>> reduce(lambda x,y:x*y,[1,2,3,4]) 24
在Python3中:
>>> reduce(lambda x,y:x*y,[1,2,3,4]) Traceback (most recent call last): File "<stdin>", line 1, in <module> NameError: name 'reduce' is not defined
原因是讲reduce函数放在了functools 模块了,需要导入。
>>> from functools import reduce >>> reduce(lambda x,y:x*y,[1,2,3,4]) 24
使用reduce可以实现这样一个功能。
将[1,2,3,4,5,6,7,8]变成 12345678,列表内是整型,所以不能用join方法。
>>> reduce(lambda x,y:10*x+y,[1,2,3,4,5,6,7,8]) 12345678
完美!
六 列表推导式
可能和filter函数能实现一样的效果。
>>> list(filter(lambda x:x%2==0,[1,2,3,4,5,6,7,8])) [2, 4, 6, 8] >>> seq=[1,2,3,4,5,6,7,8] >>> [x for x in seq if x%2==0] [2, 4, 6, 8]