函数
一、函数定义
python中用def关键字来定义函数
def fun(形参列表):
语句块
二、形参,实参
形参:形式参数,在定义函数时括号中定义的参数
实参:实际参数,在调用函数时实际传入的参数
三、传参
python的传参采用的是一种“传对象引用”的方式,传参传的是对象的引用,如果传的是不可变对象,在函数中改变参数的值时,会创建新的对象,并不会改变原始对象,相当于“值传递”;如果传的时可变对象,在函数中改变参数的值时,会直接修改原始对象,相当于“传引用”
#! /usr/bin/python3
# -*- congfig:utf-8 -*-
def fun(n,l):
n += 1
l.append(10)
print("不可变对象-修改后的对象%s"%n)
print("可变对象-原对象%s"%l)
if __name__ == "__main__":
a = 1
list1 = [1,2,3,4]
fun(a,list1)
print("不可变对象-原对象%s"%a)
print("可变对象-原对象%s"%list1)
执行结果:
不可变对象-修改后的对象2
可变对象-原对象[1, 2, 3, 4, 10]
不可变对象-原对象1
可变对象-原对象[1, 2, 3, 4, 10]
四、参数类型
1、位置参数(传实参时):
比传参数
传递实参的数量和位置必须和定义时的形参一一对应
2、关键字参数(传实参时)
比传参数
采用“形参名=实参”的方式传递实参,这种方式不需要与定义时的形参位置一一对应,python会自动根据形参名匹配
注意:关键字参数必须在位置参数后面
3、默认参数(定义形参时)
非比传参数
在定义形参时就要为其设置默认值,可以传参也可以不传参,对变化较小的参数可以定义成默认参数
注意:
1、默认参数在定义时应该放在形参的右面
2、默认参数同城应该定义成不可变类型
4、可变长参数
非比传参数
可变长参数指的是参数的个数不固定
由于传实参有按位置和按关键字两种方式,所以可变长参数也有两种
4.1、*args
定义形参时在参数名前加一个“”号,通常用args表示
它把传入的元素存放在一个元祖里,不限制传入参数的个数,用作参数比较多的时候
在传实参时,可以传递多个值,也可以传入一个元祖(列表、range(x))前面加一个“*****”
#! /usr/bin/python3
# -*- congfig:utf-8 -*-
def fun(*args):
print(args)
print(type(args))
if __name__ == "__main__":
a = 1
list1 = [1,2,3,4]
fun(1,2,3,4)
fun(*list1) #列表前必须加*
fun(*range(10))
4.2、**kwargs
定义形参时在参数名前加两个“*”号,通常用**kwargs表示
它把传入的元素存放在一个字典里
在传实参时,可以传递多个值必须时k=v的形式,也可以传入一个字典前面加两个“*****”
#! /usr/bin/python3
# -*- congfig:utf-8 -*-
def fun(**kwargs):
print(kwargs)
print(type(kwargs))
if __name__ == "__main__":
fun(a=1,b=2,c=3)
dict1 = {"a":1,"b":2,"c":3}
fun(**dict1) #字典前必须加**
4.3、(*args,**kwargs)
(*args,**kwargs)表示可以传递任何类型任何数量的参数
5、命名关键字参数
定义形参时,在必传参数后面以“”分割,后定义的参数,必须被传值(有默认值的除外),且必须按照关键字实参的形式传递
#! /usr/bin/python3
# -*- congfig:utf-8 -*-
def fun(a,b,*,c,d):
print(a,b,c,d)
if __name__ == "__main__":
fun(1,2,c=3,d=4) #c和d必须以k=v的形式传参,否则报错
6、参数的组合使用
如果一个函数中要使用多种参数
必传参数+默认参数+*args+命名关键字参数+**kwargs
五、函数的返回值 return语句
return语句用来给定函数的返回值,并结束函数
一个函数中可以有多个return语句,但最多只有一个会生效
如果函数没有return语句,则默认返会None
六、嵌套函数
函数内定义函数,就是嵌套函数
内函数只能在外函数内部调用
def one():
def two(): #定义内函数
pass
two() #调用内函数
七、命名空间和作用域
1、命名空间
命名空间:从python解释器开始执行之后,就在内存中开辟了一个空间,每当遇到一个变量的时候,就把变量名和值之间的对应关系记录下来。但是当遇到函数定义的时候解释器只是象征性的将函数名读入内存,表示知道这个函数的存在了,至于函数内部的变量和逻辑解释器根本不关心。等执行到函数调用的时候,python解释器会再开辟一块内存来存储这个函数里的内容,这个时候,才关注函数里面有哪些变量,而函数中的变量会存储在新开辟出来的内存中。函数中的变量只能在函数的内部使用,并且会随着函数执行完毕,这块内存中的所有内容也会被清空。
三种命名空间
- 内置名称:Python 语言内置的名称,比如函数名 abs、char 和异常名称 BaseException、Exception 等等
- 全局名称:模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量
- 局部名称:函数中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量
命名空间的生命周期
命名空间的生命周期取决于对象的作用域,如果对象执行完成,则该命名空间的生命周期就结束
无法从外部命名空间访问内部命名空间的对象
命名空间的加载顺序
内置命名空间(程序运行前加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载)
命名空间的查找流程
- 系统每次执行一个函数时,就会创建新的局部命名空间
- 局部命名空间代表一个局部环境,其中包含函数参数的名称和在函数体内被赋值的变量名称
- 解析名称时,解释器将首先搜索局部命名空间,如果没有找到匹配的名称,就会搜索全局命名空间
- 如果在全局命名空间未找到,则搜索内置命名空间。如果仍然找不到,就会引发NameError异常
2、作用域
作用域就是作用范围,按照生效范围可以分为全局作用域和局部作用域
全局作用域:包含内置名称空间、全局名称空间,在整个文件的任意位置都能被引用、全局有效
局部作用域:局部名称空间,只能在局部范围内生效
3、局部变量,全局变量
局部变量:在局部作用域内定义的变量(函数内部定义)
全局变量:在函数外定义的变量(python文件内一般顶格定义的变量)
4、不同作用域的变量访问原则
- 内部作用域可以访问外部作用域的变量,但不能对外部作用域的变量重新赋值(函数内可以获取到外部变量的值,但不能重新赋值)
- 外部作用域无法访问内部作用域的变量
5、global、nonlocal
global:在函数中通过global关键字声明某个变量时全局变量,可以在函数内对全局变量重新赋值
nonlocal:在嵌套函数中,内部函数要重新对外部函数中的变量赋值,需要先用nonlocal关键字声明
#! /usr/bin/python3
# -*- congfig:utf-8 -*-
a = 1 #定义全局变量a
def func():
print(a) #访问全局变量a
global a #声明a是全局变量
a = 2 #修改全局变量a
if __name__ == "__main__":
print(a)
func()
print(a)
#! /usr/bin/python3
# -*- congfig:utf-8 -*-
def one():
a = 1 #定义外部函数变量a
def two():
print(a) #访问外部函数变量a
nonlocal a #声明a时外部函数变量
a += 1 #为外部函数变量a重新赋值
print(a)
two() #调用内部函数
print(a)
if __name__ == "__main__":
one()
八、高价函数
一个函数可以作为参数传递给另一个函数,或者一个函数的返回值为另一个函数(若返回值是该函数本身,则为递归),满足任一条件则为高价函数
1、参数为函数
#! /usr/bin/python3
# -*- congfig:utf-8 -*-
def func(l): #传入一个列表
for i in l:
print(i)
def test(fun,l):
fun(l)
if __name__ == "__main__":
test(func,[1,2,3,4,5,6])
2、返会值为函数
#! /usr/bin/python3
# -*- congfig:utf-8 -*-
def func(l): #传入一个列表
for i in l:
print(i)
def test():
return func
if __name__ == "__main__":
list1 = [1,2,3,4,5,6]
a = test()
a(list1)
九、闭包
- 在函数内部再定义一个函数,并且这个函数用到了外边函数的变量,那么将这个函数以及用到的一些变量称之为
- 内部函数对外部函数作用域里变量的引用(非全局变量),则称内部函数为闭包
形成闭包的条件:
- 必须有一个嵌套函数(函数内部的函数)
- 内部函数必须引用外部函数中定义的变量
- 外部函数必须返回内部函数
#! /usr/bin/python3
# -*- congfig:utf-8 -*-
def one(l):
a = 1
def two():
#nonlocal a,l 如果内函数内要对外函数的变量重新赋值,则需要用nonlocal声明
s = a * l
print(s)
return two
if __name__ == "__main__":
l = 10
fun = one(l)
fun()
十、装饰器
装饰器的本质:是一个闭包函数
装饰器的功能:在不修改原函数及其调用方式的情况下对原函数功能进行扩展
一个装饰器
#! /usr/bin/python3
# -*- congfig:utf-8 -*-
import time
def times(fun):
def inter(*args,**kwargs):
starttime = time.time()
print("%s开始执行"%fun.__name__)
res = fun(*args,**kwargs)
stoptime = time.time()
print("%s执行结束"%fun.__name__)
print("%s的执行时间为%2f"%(fun.__name__,stoptime-starttime))
return res
return inter
@times
def test(a,b):
print(a + b)
if __name__ == "__main__":
test(3,5)
多个装饰器修饰一个函数
#! /usr/bin/python3
# -*- congfig:utf-8 -*-
def test1(fun):
def inner(*args,**kwargs):
print("第一个装饰器开始")
res = fun(*args,**kwargs)
print("第一个装饰器结束")
return res
return inner
def test2(fun):
def inner(*args,**kwargs):
print("第二个装饰器开始")
res = fun(*args,**kwargs)
print("第二个装饰器结束")
return res
return inner
@test1 #test1(test2(fun))
@test2 #test2(fun)
def test(a,b):
print(a + b)
if __name__ == "__main__":
test(3,5)
多个装饰器装饰一个函数,装饰器从下到上依次执行
十一、匿名函数 lambda
- python使用lambda来创建匿名函数
- lambda时一个表达式,比def简单很多,只能封装有限的逻辑
- lambda有自己的命名空间,lambda表达式不能使用golbal和nonlocal(不能使用外部局部变量和全局变量)
语法:
lambda 参数列表 : 表达式(返回值)
>>> f = lambda x : x ** 2
>>> f(10)
100
#! /usr/bin/python3
# -*- congfig:utf-8 -*-
def test(l):
f = map(lambda x: x ** 2,l)
for i in f:
print(i)
if __name__ == "__main__":
test(range(10))