Python3学习笔记之函数对象 函数嵌套 名称空间与作用域 闭包函数 装饰器
1. 函数对象
函数可以被引用
函数可以当作一个参数传给另外一个函数
函数当作一个返回值
函数可以当作一个容器的元素
利用函数的特性,可以取代多分支的if
def foo():
print('foo')
def bar():
print('bar')
dic={
'foo':foo,
'bar':bar,
}
while True:
choice=input('>>: ').strip()
if choice in dic:
dic[choice]()
2. 函数嵌套
1. 嵌套调用
def max(x,y):
return x if x > y else y
def max4(a,b,c,d):
res1=max(a,b)
res2=max(res1,c)
res3=max(res2,d)
return res3
print(max4(1,2,3,4))
2. 嵌套定义
def f1():
def f2():
def f3():
print('from f3')
f3()
f2()
f1()
3. 名称空间与作用域
1. 什么是名称空间?
存放名字与值绑定关系的地方(内存)
2. 加载顺序
python test.py #1、python解释器先启动,因而首先加载的是:内置名称空间 #2、执行test.py文件,然后以文件为基础,加载全局名称空间 #3、在执行文件的过程中如果调用函数,则临时产生局部名称空间
3. 名字的查找顺序
局部名称空间--->全局名称空间--->内置名称空间
注意:在全局无法查看局部的,在局部可以查看全局的
4. 作用域
#1、作用域即范围
- 全局范围(内置名称空间与全局名称空间属于该范围):全局存活,全局有效
- 局部范围(局部名称空间属于该范围):临时存活,局部有效
#2、作用域关系是在函数定义阶段就已经固定的,与函数的调用位置无关,如下
x=1
def f1():
def f2():
print(x)
return f2
x=100
def f3(func):
x=2
func()
x=10000 #已经在全局名称空间定义了,局部变量不会起作用
f3(f1())
#3、查看作用域:globals(),locals()
LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__
locals 是函数内的名字空间,包括局部变量和形参
enclosing 外部嵌套函数的名字空间(闭包中常见)
globals 全局变量,函数定义所在模块的名字空间
builtins 内置模块的名字空间
4. 闭包函数
1. 什么是闭包?
内部函数包含对外部作用域而非全局作用域的引用
2. 闭包的意义
返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,优先使用自己外层包裹的作用域
3. 例子一
def outer():
def inner():
x=2
return x
return inner
f=outer()
print(f())
4. 例子二
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'))
5. 查看闭包的元素
def outer(x,y):
def inner():
print(x,y)
return inner
f=outer(1,2)
print(f.__closure__[0].cell_contents)
print(f.__closure__[1].cell_contents)
print(f.__closure__) #这是一个元组
5. 装饰器
1. 为何要用装饰器
开放封闭原则:对修改封闭,对扩展开放
2. 什么是装饰器
装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。
强调装饰器的原则:1 不修改被装饰对象的源代码 2 不修改被装饰对象的调用方式
装饰器的目标:在遵循1和2的前提下,为被装饰对象添加上新功能
3. 装饰器的使用
1. 简单装饰器的使用
import time
def timmer(func):
def inner():
start_time=time.time()
func()
stop_time=time.time()
print ("the use time is %s" %(stop_time-start_time))
return inner
@timmer #在被装饰对象上面,添加一行 相当于index=timmer(index)
def index():
time.sleep(3)
print("this is the index page.")
index()
2. 带有参数的装饰器
import time
def timmer(func):
def inner(*args,**kwargs):
start_time=time.time()
func(*args,**kwargs)
stop_time=time.time()
print ("the use time is %s" %(stop_time-start_time))
return inner
@timmer
def home(name): #home=timmer(home)
time.sleep(4)
print("this is the %s page." %name)
home('yangjianbo')
3. 原函数有返回值
import time
def timmer(func):
def inner(*args,**kwargs):
start_time=time.time()
res=func(*args,**kwargs)
stop_time=time.time()
print ("the use time is %s" %(stop_time-start_time))
print(res)
return inner
@timmer
def home(name): #home=timmer(home)
time.sleep(4)
print("this is the %s page." %name)
return 123
res=home('yangjianbo')
4. 原函数有注释信息
import time
from functools import wraps
def timmer(func):
@wraps(func) #加在最内层函数正上方
def inner(*args,**kwargs):
# inner.__doc__=func.__doc__ #如果有@wraps,这一句就可以注释掉了。
start_time=time.time()
res=func(*args,**kwargs)
stop_time=time.time()
print ("the use time is %s" %(stop_time-start_time))
print(res)
return inner
@timmer
def home(name): #home=timmer(home)
'''
这是测试代码
:param name:
:return:
'''
time.sleep(4)
print("this is the %s page." %name)
return 123
res=home('yangjianbo')
print(home.__doc__)
5. 无参数验证
import time
login_info={'username':None,'login_status':False}
def auth(func):
def inner(*args,**kwargs):
if login_info['username'] != None and login_info['login_status'] is True:
print ("login sucessful")
res=func(*args,**kwargs)
print (res)
else:
user=input("user:").strip()
password=input("password:").strip()
if user == 'yangjianbo' and password == '123':
print ("login sucessful")
login_info['username'] =user
login_info['login_status'] =True
res=func(*args,**kwargs)
print (res)
return inner
@auth
def index():
time.sleep(3)
print("this is the index page.")
return 'zuiniubi'
index()
@auth
def home(name):
time.sleep(4)
print("this is the %s page." %name)
return 'niubi'
home('yangjianbo')
6. 有参装饰器(使用三层装饰器)
import time
login_info={'username':None,'login_status':False}
def auth(engine='file'):
# engine='file'
def wrapper(func):
def inner(*args,**kwargs):
if login_info['username'] != None and login_info['login_status'] is True:
print ("login sucessful")
res=func(*args,**kwargs)
print (res)
else:
user=input("user:").strip()
password=input("password:").strip()
if engine=='file':
if user == 'yangjianbo' and password == '123':
print ("login sucessful")
login_info['username'] =user
login_info['login_status'] =True
res=func(*args,**kwargs)
print (res)
elif engine=='mysql':
print ("mysql auth")
elif engine=='ldap':
print ('ldap auth')
return inner
return wrapper
@auth(engine='ldap')
def index():
time.sleep(3)
print("this is the index page.")
return 'zuiniubi'
index()
@auth(engine='file')
def home(name):
time.sleep(4)
print("this is the %s page." %name)
return 'niubi'
home('yangjianbo')
7. 叠加多个装饰器
def outter1(func1): #func1=wrapper2的内存地址
print('加载了outter1')
def wrapper1(*args,**kwargs):
print('执行了wrapper1')
res1=func1(*args,**kwargs)
return res1
return wrapper1
def outter2(func2): #func2=wrapper3的内存地址
print('加载了outter2')
def wrapper2(*args,**kwargs):
print('执行了wrapper2')
res2=func2(*args,**kwargs)
return res2
return wrapper2
def outter3(func3): # func3=最原始的那个index的内存地址
print('加载了outter3')
def wrapper3(*args,**kwargs):
print('执行了wrapper3')
res3=func3(*args,**kwargs)
return res3
return wrapper3
@outter1 # outter1(wrapper2的内存地址)======>index=wrapper1的内存地址
@outter2 # outter2(wrapper3的内存地址)======>wrapper2的内存地址
@outter3 # outter3(最原始的那个index的内存地址)===>wrapper3的内存地址
def index():
print('from index')
print('======================================================')
index()
6. 关键字
1. global
在局部作用域修改全局作用域的变量值。
例子:
x=10
def name():
x=100
name()
print(x)
# x=100在局部作用域内有效,虽然执行了函数,但是在全局作用域的x的值,依然是x=10,要想让x=100,必须在函数内部添加一个关键字global
x=10
def name():
global x
x=100
name()
print(x)
7. 装饰器练习
一,编写函数,(函数的执行时间是随机的)
import time
def a():
x = time.strftime('%Y-%m-%d %X')
print(x)
a()
结果:
D:\Python36\python.exe F:/python/第一课/while-lianxi.py
2017-10-14 08:40:11
二,编写装饰器,为函数加上统计时间的功能。
#这种是采用传参的方式,虽然没有修改index和home函数的源代码,但是修改了它们的调用方式。所以需要采用装饰器。
import time
def timmer(tongji_hanshu):
# tongji_hanshu=home
def inner():
start_time=time.time()
tongji_hanshu()
stop_time=time.time()
print("函数执行时间: %s" %(stop_time-start_time))
return inner
@timmer #语法糖
def index():
time.sleep(3)
print("welcome to index page!")
index()
@timmer
def home():
time.sleep(5)
print("welcome to home page!")
home()
结果:
D:\Python36\python.exe F:/python/第一课/while-lianxi.py
welcome to index page!
函数执行时间: 3.0001718997955322
welcome to home page!
函数执行时间: 5.000285863876343
三. 编写装饰器,为函数加上认证的功能。
import time
def outter(func):
def auth():
username=input("输入用户名: ").strip()
password=input("输入密码: ").strip()
if username=='yangjianbo' and password=='123':
print ("登录成功!")
func() #这么写死了
else:
print("登录失败!")
return auth
@outter
def index():
time.sleep(3)
print("welcome to index page!")
index()
结果:
D:\Python36\python.exe F:/python/第一课/while-lianxi.py
输入用户名: yangjianbo
输入密码: 123
登录成功!
welcome to index page!
四:编写装饰器,为多个函数加上认证功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码。
#这种情况是针对index,没有参数的函数,如果被装饰的函数有参数,如何修订装饰器。
import time
def outter(func):
def auth(*args,**kwargs):
username=input("输入用户名: ").strip()
password=input("输入密码: ").strip()
if username=='yangjianbo' and password=='123':
print ("登录成功!")
func(*args,**kwargs) #这么写死了
else:
print("登录失败!")
return auth
@outter
def index():
time.sleep(3)
print("welcome to index page!")
index()
@outter #home=outter(home)
def home(username):
time.sleep(2)
print("welcome %s to home page!" %username)
home('yangjianbo')
结果:
D:\Python36\python.exe F:/python/第一课/while-lianxi.py
输入用户名: yangjianbo
输入密码: 123
登录成功!
welcome to index page!
输入用户名: yangjianbo
输入密码: 123
登录成功!
welcome yangjianbo to home page!
根据题目要求:只要登录成功,以后就不需要再登录。所以上面的代码还需要修改。
import os
import time
login_status=False
def outter(func):
def auth(*args,**kwargs):
if login_status is True:
return func(*args,**kwargs)
else:
username=input("输入用户名: ").strip()
password=input("输入密码: ").strip()
with open('account.txt','r',encoding='utf-8') as accout:
accout=accout.read().strip('\r\n').split('|')
if username==accout[0] and password==accout[1]:
print ("登录成功!")
login_status=True
func(*args,**kwargs)
else:
print("登录失败!")
return auth
@outter #index=outter(index)
def index():
time.sleep(3)
print("welcome to index page!")
index()
@outter
def home(username):
time.sleep(2)
print("welcome %s to home page!" %username)
home('yangjianbo')
结果:
D:\Python36\python.exe F:/python/第一课/while-lianxi.py
Traceback (most recent call last):
File "F:/python/第一课/while-lianxi.py", line 158, in <module>
index()
File "F:/python/第一课/while-lianxi.py", line 140, in auth
if login_status is True:
UnboundLocalError: local variable 'login_status' referenced before assignment
上面报错的分析:说局部变量login_status在引用之前没有指定.
我明明定义在全局作用域了,为什么报错?
解决方法:在函数内,添加一个关键字global,表示引用全局变量。
修改后的代码:
import os
import time
login_status=False
def outter(func):
def auth(*args,**kwargs):
global login_status
if login_status is True:
return func(*args,**kwargs)
else:
username=input("输入用户名: ").strip()
password=input("输入密码: ").strip()
with open('account.txt','r',encoding='utf-8') as accout:
accout=accout.read().strip('\r\n').split('|')
if username==accout[0] and password==accout[1]:
print ("登录成功!")
login_status=True
func(*args,**kwargs)
else:
print("登录失败!")
return auth
@outter #index=outter(index)
def index():
time.sleep(3)
print("welcome to index page!")
index()
@outter
def home(username):
time.sleep(2)
print("welcome %s to home page!" %username)
home('yangjianbo')
结果:没有报错了。
第四个装饰器最后版本:
import time
user_status={"user":None,"login_status":False}
def outter(func):
def auth(*args,**kwargs):
if user_status['user'] and user_status['login_status']:
res=func(*args, **kwargs)
return res
username=input("输入用户名: ").strip()
password=input("输入密码: ").strip()
if username=='yangjianbo' and password=='123':
print ("登录成功!")
user_status['user']=username
user_status['login_status']=True
res=func(*args, **kwargs)
return res
else:
print("登录失败!")
return auth
@outter
def index():
time.sleep(3)
print("welcome to index page!")
# return 123
index()
@outter
def home(username):
time.sleep(2)
print("welcome to %s home page" % username)
# return 456
home('yangjianbo')
练习五 编写装饰器,为多个函数加上认证功能,要求登录成功一次,在超时时间内无需重复登录,超过了超时时间,则必须重新登录
import time
import random
user={"user":None,"login_time":None,'timeout':0.03}
def timmer(func):
def inner(*args,**kwargs):
start_time=time.time()
res=func(*args,**kwargs)
stop_time=time.time()
print('%s' %(stop_time-start_time))
return res
return inner
def outter(func):
def auth(*args,**kwargs):
if user['user']:
timeout=time.time()-user['login_time']
if timeout<user['timeout']:
res=func(*args, **kwargs)
return res
username=input("输入用户名: ").strip()
password=input("输入密码: ").strip()
if username=='yangjianbo' and password=='123':
user['user']=username
user['login_time']=time.time()
res=func(*args, **kwargs)
return res
else:
print("登录失败!")
return auth
@outter
def index():
time.sleep(random.randrange(3))
print("welcome to index page!")
index()
@outter
def home(username):
time.sleep(random.randrange(3))
print("welcome to %s home page" % username)
home('yangjianbo')
练习六:编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果
from urllib.request import urlopen
def outer(url):
def get():
f=urlopen(url).read()
return f
return get
baidu=outer('http://www.baidu.com')
sohu=outer('http://www.sohu.com')
baidu=baidu()
sohu=sohu()
练习七:拿到下载的页面,保存到一个文件中。
from urllib.request import urlopen
def outer(url):
def get():
f=urlopen(url).read()
return f
return get
baidu=outer('http://www.baidu.com')
sohu=outer('http://www.sohu.com')
baidu=baidu()
sohu=sohu()
with open('baidu.html','w',encoding='utf-8') as f:
f.write(baidu.decode('utf-8'))

浙公网安备 33010602011771号