第五章 Python之装饰器
函数对象
函数是第一类对象:即函数可以当作数据传递
#可以被引用,可以被当作参数传递,返回值可以是函数,可以当作容器类型的元素
#引用
def func(x,y):
print(x,y)
f=func()
print(f)
#参数传递
def bar(func):
func()
bar(foo)
#返回值
def foo():
print('from foo')
def bar():
return foo
# 容器类型的元素(利用该特性,可以利用函数取代分支的if)
def fun1():
print('fun1')
def fun2():
print('fun2')
di={
'func':fun1,
'fun2':fun2,
}
while True:
choice=input(': '.strip())
if choice in di:
dic[choice]()
函数的嵌套
函数可以嵌套调用,也可以嵌套定义
#函数的嵌套调用
def max(x,y):
return x if x > y else y
def max4(a,b,c,d):
res1=max(a,b)
res2=max(c,res1)
res3=max(d,res2)
return res3
print(max4(2,3,4,8))
#函数的嵌套定义
def f1():
def f2():
def f3():
return 1
f3()
f2()
名称空间与作用域
名称空间是存放名字与值绑定关系的地方
名称空间的加载顺序为:python解释器启动加载内置名称空间->执行.py文件,加载全局名称空间->调用函数加载局部名称空间
名字查找顺序:局部名称空间->全局名称空间->内置名称空间
作用域:全局范围(全局名称空间和内置名称空间),局部范围(局部名称空间)
作用域关系在函数定义阶段就已经固定,与函数的调用位置无关
x=1
def f1():
def f2():
print(x)
return f2
x=100
def f3(func):
x=2
func()
x=1000
f3(f1())
#查看作用域:globals(),locals()
def func():
xxxxxx=111
print(globals())
print(locals())
func()
# LEGB代表名字查找顺序:locals-enclosing function-globals-__builtins__
# locals:函数内的名字空间,包括局部变量和形参
# enclosing:外部嵌套函数的名字空间
# globals:全局变量,函数定义所在模块的名字空间
# builtins:内置模块的名字空间
global 与 nonlocal 关键字
x=100
def func():
global x #修改全局变量
x=1
func()
print(x)
x='global'
def f1():
x=1
def f2():
nonlocal x #修改本函数上一层函数的变量值(只在函数内找,不在外面找)
x=0
f2()
print('f1...x',x)
f1()
print(x)
闭包函数
定义在函数内部的函数,内部函数包含对外部作用域而非全局作用域的引用,通常将闭包函数用return返回,然后可以任意调用
闭包函数的意义:一种新的给函数传参的方式,返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,使得该函数无论在何处调用,优先使用自己外层包裹的作用域
def outer():
x='hi'
y='hey'
def inner():
print(x,y)
return inner
f=outer()
print(f.__closure__[0].cell_contents) #f的外层作用域关系所引用的值
print(f.__closure__[1].cell_contents)
def counter():
n=0
def incr():
nonlocal n
x=n
n+=1
return x
return incr
c=counter()
print(c())
print(c())
print(c())
print(c.__closure__[0].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
def timmer(func):
def wrapper(*args,**kwargs):
start_time=time.time()
res=func(*args,**kwargs)
stop_time=time.time()
print('run time is %s'%(stop_time-start_time))
return res
return wrapper @timmer #foo=timmer(foo)
def foo():
time.sleep(3)
print('from foo')
foo()
#有参装饰器
def auth(driver='file'):
def auth2(func):
def wrapper(*args,**kwargs):
name=input('user: ')
pwd=input('pwd: ')
if driver == 'file':
if name == 'egon' and pwd == '':
print('login sucess')
res=func(*args,**kwargs)
return res
elif driver == 'ldap':
print('ldap')
return wrapper
return auth2 @auth(driver='file')
def foo(name):
print(name) foo('egon')
#装饰器语法
@deco1
@deco2
@deco3
def foo():
pass
foo=deco1(deco2(deco3(foo())))
#装饰器补充:wraps
from functools import wraps
def deco(func):
@wraps(func)
def wrapper(*args,**kwargs):
return func(*args,**kwargs)
return wraps @deco
def index():
'''哈哈哈'''
print('from index') print(index.__doc__)
练习
(1)编写函数,函数执行的时间是随机的
import time,random
def func():
time.sleep(random.randint(0,10))
print('sucess')
func()
(2)编写装饰器,为函数加上统计时间的功能
import time def timmer(func):
#func=index #原始的index
def inner(*args,**kwargs): #接收任意长度和形式的参数传给func使用
start=time.time()
res=func(*args,**kwargs) #调用原始的index()
stop=time.time()
print('run time is %s'%(stop-start))
return res
return inner @timmer #index=timmer(index)
def index(name):
time.sleep(3)
print('welcome %s to index'%name)
return 111 res=index('luoli') #inner()
print(res)
(3)编写装饰器,为函数加上认证的功能
import time def auth2(engine='file'):
#engine='file'
def auth(func):
#func=index()
def inner(*args,**kwargs):
if engine == 'file':
name=input('name:').strip()
pwd=input('pwd:').strip()
if name == 'luoli' and pwd == '':
print('login sucess')
return func(*args,**kwargs)
else:
print('login failed')
elif engine == 'mysql':
print('mysql')
elif engine == 'ldap':
print('ldap')
else:
print('engine not exists')
return inner
return auth @auth2(engine='mysql') #@auth #index=auth(index) #index=inner
def index(name):
time.sleep(1)
print('welcome %s to index'%name)
return 111 res=index('luoli')
print(res)
(4)编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
t='user.txt'
login_status={"name":None,"pwd":None}
def auth(auth_type='file'):
def auth2(func):
def wrapper(*args,**kwargs):
if login_status['name'] and login_status['pwd']:
return func(*args,**kwargs)
if auth_type=='file':
with open(t,'r',encoding='utf-8') as f:
d=eval(f.read())
name=input('Please enter your name:').strip()
pwd=input('Please input your pwd:').strip()
if name == d["name"] and pwd == d["pwd"]:
login_status["name"]=name
login_status["pwd"]=pwd
return func(*args,**kwargs)
else:
print('username or pwd error')
elif auth_file=='sql':
pass
else:
pass
return wrapper
return auth2 @auth()
def index():
print('sucess')
return 123
@auth()
def home(name):
print('welcome %s to my home'%name) index()
home('hi')
(5)编写装饰器,为多个函数加上认证功能,要求登录成功一次,在超时时间内无需重复登录,超过了超时时间,则必须重新登录
import time,random user={'user':None,'login_time':None,'timeout':0.000003} def auth(func):
def wrapper(*args,**kwargs):
if user["user"]:
timeout = time.time() - user['login_time']
if timeout < user["timeout"]:
return func(*args, **kwargs)
name=input('Please input your name:').strip()
pwd=input('Please input your pwd:').strip()
if name=='luoli' and pwd=='':
user["user"]=name
user["login_time"]=time.time()
return func(*args,**kwargs)
return wrapper @auth
def index():
time.sleep(random.randrange(3))
print('welcome to index') @auth
def home(name):
time.sleep(random.randrange(3))
print('welcome %s to home'%name)
index()
home('luoli')
(6)编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果
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'))
(7)为题目六编写装饰器,实现缓存网页内容的功能,实现下载的页面存放于文件中,如果文件内有值(文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后存到文件中
import requests
import os cache_file='cache.txt'
def make_cache(func):
def wrapper(*args,**kwargs):
if not os.path.exists(cache_file):
with open(cache_file,'w'):pass if os.path.getsize(cache_file):
with open(cache_file,'r',encoding='utf-8') as f:
res=f.read() else:
res=func(*args,**kwargs)
with open(cache_file,'w',encoding='utf-8') as f:
f.write(res)
return res
return wrapper @make_cache
def get(url):
return requests.get(url).text res=get('http://baidu.com')
print(res)
#扩展功能:用户可以选择缓存介质/缓存引擎,针对不同的url,缓存到不同的文件中
import requests,os,hashlib
engine_settings={
'file':{'dirname':'./db'},
'mysql':{
'host':'127.0.0.1',
'port':3306,
'user':'root',
'password':''},
'redis':{
'host':'127.0.0.1',
'port':6379,
'user':'root',
'password':''},
} def make_cache(engine='file'):
if engine not in engine_settings:
raise TypeError('egine not valid')
def deco(func):
def wrapper(url):
if engine == 'file':
m=hashlib.md5(url.encode('utf-8'))
cache_filename=m.hexdigest()
cache_filepath=r'%s/%s' %(engine_settings['file']['dirname'],cache_filename) if os.path.exists(cache_filepath) and os.path.getsize(cache_filepath):
return open(cache_filepath,encoding='utf-8').read() res=func(url)
with open(cache_filepath,'w',encoding='utf-8') as f:
f.write(res)
return res
elif engine == 'mysql':
pass
elif engine == 'redis':
pass
else:
pass return wrapper
return deco @make_cache(engine='file')
def get(url):
return requests.get(url).text # print(get('https://www.python.org'))
print(get('https://www.baidu.com'))
(8)用函数对象的概念,制作一个函数字典,在文件开头声明一个空字典,然后在每个函数前加上装饰器,完成自动添加到字典的操作
route_dic={} def make_rout(name):
def deco(func):
route_dic[name]=func
return deco @make_rout('select')
def func1():
print('select') @make_rout('insert')
def func2():
print('insert') @make_rout('update')
def func3():
print('update') @make_rout('delete')
def func4():
print('delete') print(route_dic)
(9)编写日志装饰器,实现功能如:一旦函数f1执行,则将消息2017-07-21 11:12:11 f1 run写入到日志文件中,日志文件路径可以指定(注意:时间格式的获取time.strftime('%Y-%m-%d %X'))
import time
import os def logger(logfile):
def deco(func):
if not os.path.exists(logfile):
with open(logfile,'w'):pass def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
with open(logfile,'a',encoding='utf-8') as f:
f.write('%s %s run\n' %(time.strftime('%Y-%m-%d %X'),func.__name__))
return res
return wrapper
return deco @logger(logfile='a.txt')
def index():
print('index') index()
第五章 Python之装饰器的更多相关文章
- python开发装饰器的应用
python全栈开发-Day10 装饰器(闭合函数的应用场) 一. 装饰器 装饰器就是闭包函数的一种应用场景 什么是闭包函数?我们再来回忆一下: 闭包函数: 定义在函数内部的函数,并且该函数包含对 ...
- python之装饰器函数
本章内容 引入 装饰器的形成过程 开放封闭原则 谈装饰器主要功能和装饰器固定结构 带参数的装饰器 多个装饰器装饰一个函数 引入 作为一个会写函数的python开发,我们从今天开始要去公司上班了.写了一 ...
- 13.Python略有小成(装饰器,递归函数)
Python(装饰器,递归函数) 一.开放封闭原则 软件面世时,不可能把所有的功能都设计好,再未来的一两年功能会陆续上线,定期更新迭代,软件之前所用的源代码,函数里面的代码以及函数的调用方式一般不 ...
- python之装饰器的两种写法
上一篇文章介绍了 装饰器的概念.现在讲一下在程序中怎么来写装饰器.上代码: def X(fun): def Y(b): print(b) fun() return Y def test(): prin ...
- Python各式装饰器
Python装饰器,分两部分,一是装饰器本身的定义,一是被装饰器对象的定义. 一.函数式装饰器:装饰器本身是一个函数. 1.装饰函数:被装饰对象是一个函数 [1]装饰器无参数: a.被装饰对象无参数: ...
- Python札记 -- 装饰器补充
本随笔是对Python札记 -- 装饰器的一些补充. 使用装饰器的时候,被装饰函数的一些属性会丢失,比如如下代码: #!/usr/bin/env python def deco(func): def ...
- python基础——装饰器
python基础——装饰器 由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数. >>> def now(): ... print('2015-3-25 ...
- 【转】详解Python的装饰器
原文链接:http://python.jobbole.com/86717/ Python中的装饰器是你进入Python大门的一道坎,不管你跨不跨过去它都在那里. 为什么需要装饰器 我们假设你的程序实现 ...
- 两个实用的Python的装饰器
两个实用的Python的装饰器 超时函数 这个函数的作用在于可以给任意可能会hang住的函数添加超时功能,这个功能在编写外部API调用 .网络爬虫.数据库查询的时候特别有用 timeout装饰器的代码 ...
随机推荐
- UWP添加数字证书导出安装包本地安装
先生成一个简单的HelloWorld应用程序 <Page x:Class="HelloWorld.MainPage" xmlns="http://schemas.m ...
- VS2013 opencv 无法删除“继承的值”问题解决方案
http://www.360doc.com/content/15/0103/14/110467_437739376.shtml 解决方案: (1)视图——其他窗口——属性管理器. 注意:是”属性管理器 ...
- When you hit a wall, just kick it in.
Teach Yourself Programming in Ten Years. ----- Peter Norvig Teach Yourself Programming in Ten Years ...
- Python笔记2----包、模块、库、条件循环
包.模块.库 包指的是 : 文件夹 模块和库就是.py文件. 其中,库是一些功能相同的模块的集合,其也是模块. 1.import 模块 2.form 库 import 模块 3.import 包.子包 ...
- 关于mvc架构的浅谈
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑.数据.界面显示分离的方法组织代码 ...
- python中的各个包的安装,用pip安装whl文件
在安装之前,一直比较迷惑究竟如何用,安装完后,发现竟然如此简单 首先我一般用的是python27,官网下载python27msi安装window7 64位后,已经自带了Pip 和 easy_insta ...
- hdu 3177贪心
#include<stdio.h>/*只能按这种形式排序单纯一种形式是不对的,按ai排序 20 2 1 1 10 20 按bi排序 20 2 5 17 1 16 都是不对的 二a.u+b. ...
- sw算法求最小割学习
http:// blog.sina.com.cn/s/blog_700906660100v7vb.html 转载:http://www.cnblogs.com/ylfdrib/archive/201 ...
- [Windows Server]新机子上装老系统·
硬盘模式改了也得用U大师,然后再PE里装 1.U大师做启动盘 2.拷贝解压后的系统进去 3.用PE自带安装工具
- 9patch生成图片
private Bitmap get_ninepatch(int id,int x, int y, Context context){ // id is a resource id for a val ...