函数复习
函数的使用规则:
函数的使用必须遵循:先定义后使用的原则
#函数的定义,与变量的定义是相似的,如果没有事先定义函数而直接引用,就相当于在引用一个不存在变量名
所以下面代码会报错
foo() def foo(): print('in the foo') # NameError: name 'foo' is not defined
要使用一个函数需要经过两个步骤:
1.定义阶段 --> 只检测语法,不执行函数体
def foo(): print('in the foo')
2.执行阶段
foo()
所以下面代码不会报错:
def foo(): # 定义阶段,只检查语法,不执行函数体 print('in the foo') bar() def bar(): print('in the bar') foo()
x = 1 def func(): # 定义阶段,只检测语法,不执行函数体 print(x) x = 2 # 在执行之前修改了全局变量x func() # 执行函数,在函数执行之前修改了x的值,所以执行时打印的x的值为2
以下代码在定义阶段不会报错,但是在执行阶段就会报错
def func(): # 语法没问题,但是逻辑有问题,引用了一个不存在的变量名。 name func()
函数的返回值:
可以返回任意类型的值
- 没有return,返回None
- 返回一个值(可以是任意类型的)
- 返回多个值,默认以元组的形式返回
一个返回值:
def func(): return {'a':1,'b':2} res = func() print(res) print(res['b'])
多个返回值:
def func(): return {'a':1,'b':2},None,5,[6,8] res = func() print(res)
形式参数与实际参数:
形式参数也叫作形参,是在函数的调用阶段定义的,可以理解为一个变量名。
实际参数也叫作实参,是在函数得执行阶段定义的,可以理解为一个值
位置参数与关键字参数
位置参数:按照从左到右的顺序依次定义的参数
按位置定义的形参,必须都被传值。多一个或少一个都会报错。
按位置定义的实参,与形参一一对应。
关键字参数:在定义时,按照key=value的形式定义。关键字参数是在实参的角度定义的。
def func(x, y): print(x) print(y) func(4) # TypeError: func() missing 1 required positional argument: 'y' func(4,5,6) # TypeError: func() takes 2 positional arguments but 3 were given func(y=8,5) # SyntaxError: positional argument follows keyword argument func(3,x=5,y=8) # TypeError: func() got multiple values for argument 'x'
注意:
- 位置参数必须在关键字参数之前。
- 实参的形式可以死你位置参数也可以是关键字参数,但是一个形参不能重复被传值。
默认参数:
默认参数:在定义阶段,就已经为形参赋值。
def register(name,age,sex='male'): print(name,age,sex) register('张三','28') register('李四','32') register('翠花','22','female')
位置形参:每次都发生变化的值定义为位置形参。
默认参数: 通常情况下不会发生变化的值定义为默认参数
注意:
- 默认参数必须放在位置形参之后
- 默认参数通常定义为不可变数据类型
动态参数:
def func(x,y,*args): # x=1,y=2,args=(5,8,4) print(x) print(y) print(args) func(1,2,5,8,4)
def func(x,y,**kwargs): # x=1,y=2,kwargs={'a': 3, 'b': 4} print(x) print(y) print(kwargs) func(1,2,a=3,b=4)
注意:
- *:在实参阶段或函数体中有打散的作用
- *:在形参阶段有聚合的作用
命名关键字参数:
命名关键字参数:定义在*(*args)后面的形参,这类形参必须要通过,关键字参数传值。
def register(*,username,age,password): print(username) print(age) print(password) # register(username='root',password='123') # TypeError: register() missing 1 required keyword-only argument: 'age' register(username='root',password='123',age=26)
def register(*args,username,age,password): print(username) print(age) print(password) # register(1,2,3,username='root',password='123') # TypeError: register() missing 1 required keyword-only argument: 'age' register(1,2,3,username='root',password='123',age=26)
def register(*args,username,age=10,password): # 这里的age=10不叫默认参数(默认参数需要放在位置参数之后),叫做命名参数的默认值。但是它具有默认参数的效果 print(username) print(age) print(password)
register(1,2,3,username='root',password='123')
特点:
- *后面的形参必须传值。否则报错
- 这类形参必须要通过关键字参数传值
各个参数的顺序:
位置参数, 默认参数, *args, 命名关键字参数, **kwargs
def func(x,y=1,*args,z,**kwargs): print(x) # x=1 print(y) # y=2 print(args) # args = (3, 4, 5) print(z) # z=10 print(kwargs) # kwargs = {'a':1, 'b':2} func(1,2,3,4,5,z=10,a=1,b=2)
函数的参数分类:
形式参数:
1.位置形参
2.默认参数
3.动态参数
4.命名关键字参数
实际参数:
1.位置实参
2.关键字参数
3.动态参数 ????????
函数的嵌套
函数的嵌套调用:
# 求四个数的最大值 def max2(x,y): return x if x>y else y def max4(a,b,c,d): res1 = max2(a,b) res2 = max2(res1,c) res3 = max2(res2,d) return res3 res = max4(25,0,56,-18) print(res)
函数的嵌套定义:
def func1(): def func2(): def func3(): print('in the func3') print('in the func2') func3() print('in the func1') func2() func1()
命名空间:
1. 内置命名空间
Python解释器自带的,Python解释器启动就会生成。
2. 全局命名空间
Python文件级别定义的名字,执行Python文件是会产生,文件执行完后就会失效。
3. 局部命名空间
定义在函数内部的名字,只有在函数调用的时候才会生效,函数调用结束则失效。
加载顺序:
内置命名空间-->全局命名空间-->局部命名空间
查找顺序:
局部命名空间-->全局命名空间-->内置命名空间
作用域
1. 全局作用域
内置命名空间与全局命名空间的名字属于全局作用域,在整个文件的任意位置都能被引用,全局有效。
2. 局部作用域
局部命名空间属于局部作用域,只在函数内部可以被引用,局部有效。
globals()
locals()
globa x # 声明要引用和修改全局作用域中x的值
nonlocal x # 声明要引用和修改外部作用域中x的值,不包含全局作用域
示例一:
money = 200 def func(): money = 100 def tell_info(name): print("%s have money %s" % (name, money)) tell_info('egon') func() # egon have money 100
示例二:
money = 200 def tell_info(name): print("%s have money %s" %(name,money)) def func(): money = 100 tell_info('egon') func() # egon have money 200
以上两个示例可以看出,函数的作用域关系是在函数定义阶段决定的域与调用位置无关,无论函数在何处调用,都需要回到定义阶段去找对应的作用域关系。
闭包函数
定义:
函数内部定义的函数,该内部函数包含对外部作用域,而不是对全局作用域名称的引用,那么该内部函数就称为闭包函数。
闭包函数的形式:
def 外部函数名(): 内部函数需要的变量 def 内部函数名(): 引用外部变量 return 内部函数名
包一层的形式:
def func(): money = 100 def tell_info(): print("egon have money %s" % money) return tell_info tell_info = func() tell_info()
包两层的形式:
def outer(): name = 'egon' def func(): money = 10 def tell_info(): print("egon have money %s" % money) print("my name is %s" % (name)) return tell_info return func f = outer()()()
__closure__:
def func(): money = 100 name = 'egon' def tell_info(): print("egon have money %s" % money) print(name) return tell_info tell_info = func() print(tell_info.__closure__[0].cell_contents) # 查看闭包函数中的元素 print(tell_info.__closure__[1].cell_contents)
闭包函数的意义与应用:
#闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域 #应用领域:延迟计算(原来我们是传参,现在我们是包起来) from urllib.request import urlopen def index(url): def get(): return urlopen(url).read() return get baidu=index('http://www.baidu.com') print(baidu().decode('utf-8'))
装饰器
开放封闭原则:对扩展是开放的,对修改是封闭的。
装饰器的定义:
装饰器本质可以是任意可调用的对象,被装饰的对象也可以是任意可调用的对象。
装饰器的功能:
在不修改对象源代码以及调用方式的前提下为被装饰对象添加新功能。
原则:
- 不修改源代码
- 不修改调用方式
计算函数的运行时间:
闭包函数实现:
import time import random
def timer(func): def wrapper(): start_time = time.time() func() stop_time = time.time() print('run time is %s' %(stop_time - start_time)) return wrapper def index(): time.sleep(random.randrange(1,4)) print('welcome to index page') wrapper = timer(index) wrapper() # 这种方式虽然没有改变源代码,但是改变了函数的调用方式(由原来的index(),变成了timer(index)())
修改版:
import time import random
def timer(func): def wrapper(): start_time = time.time() func() stop_time = time.time() print('run time is %s' %(stop_time - start_time)) return wrapper def index(): time.sleep(random.randrange(1,4)) print('welcome to index page') def admin(): time.sleep(random.randrange(1,4)) print('welcome to admin page') index = timer(index) admin = timer(admin) index() admin()
# 现在这种方式既没有改变源代码也没有改变函数的调用方式(再使用者看来没有改变),我是每次我们需要执行一个这样的操作 xxx = timer(index)
装饰器实现:
import time import random def timer(func): def wrapper(): start_time = time.time() func() stop_time = time.time() print('run time is %s' %(stop_time - start_time)) return wrapper @timer # index = timer(index) def index(): time.sleep(random.randrange(1,4)) print('welcome to index page') @timer def admin(): time.sleep(random.randrange(1,4)) print('welcome to admin page') index() admin()
装饰器模板:
def wrapper(func): def inner(*args,**kwargs): 被装饰的函数执行之前添加的功能 res=func(*args,**kwargs) 被装饰的函数执行之前添加的功能 return res return inner
多个装饰器装饰一个函数:
多个装饰的的执行顺序是自上而下的