闭包函数
包含对外的作用域,在内部定义函数,引用自身函数外的函数,
name ='alex'
def func() :
name ='egon'
def bar() :
print(name) #name属于外层func(),引用外部的函数变量,先找外面一层的
return bar
b=func()
b()---结果 egon
闭包函数不受定义级别的限制,任何位置都可用,
f._closure_闭合的意思,所有的闭包函数都有这个 属性
特点:
自带作用域,自己就带着一层,所以先找外部作用域,
延迟计算,什么时候用,什么时候执行func()
例子:一直又钱:
def f() :
money=100
def tell_infor(name): ----
print('%s have money %s' %(name,money))-----先找tell_infor是否money有值,没有,再向外找,
tell_infor('egon')
f()
---egon have money 100
总结:想给函数一种状态,就把函数定义在内部,在这个外部定一个变量,还要不受位置限制,就用return返回,
例子2:
def b():
money=100
def c():
print('egon have money %s' %(money))
return c
c=b()
def foo():
money=1
c()
foo() ----egon have money 100
解析:money在b()已经被定义,所以在foo()再调用从C(时不会从foo()里去找,
----
闭包函数的基本形式:
def 外部函数名():
内部函数需要的变量
def 内部函数():
引用外部的变量
return 内部函数
----
def deco():
x=1 #x=1就x写死了,可以写在 deco(x)里,当做参数,
def wrapper():
print(x) #调用外面函的参数
return wrapper
wrapper=deco() #把deco()函数赋给wrapper,wrapper就有了全局作用域
wrapper() #执行wrapper函数
---结果 1
-------闭包函数的应用场景-------------
提取网页链接,网页下载
from urllib.request import urlopen ---导入模块
def url_func(url):
def wrapper():
return urlopen(url).read()
return wrapper
python=url_func('http://www.python.org')
print(python())
--- 结果 b'<!doctype html>
<!--[if lt IE 7]>
<html class="no-js ie6 lt-ie7 lt-ie8 lt-ie9"> <![endif]-->
<!--[if IE 7]> ......
============================
软件开发原则
开发封闭原则
1.对扩展是开发的,增加功能
2.对修改是封闭的,对源代码,函数,不宜修改,影响全局
=============================================
装饰器
功能:不修改被装饰对象的源代码,以及被装饰对象的调用方式的前提下,对其增加新功能。
原则:不修改源代码,不修改调用方式,
目标:增加新功能
例子:在index的基本功能上增加,函数到底运行了多久,就知道了index的网络延迟
import time #导入时间模块
import random #导入用于生成随机数模块
def timmer(func): #装饰器timmer.可以用在index函数,也可以用在别的函数上
#func=index() 可以直接当做timmer 的参数,更灵活,
def wrapper():
start_time=time.time() #功能开始时间,
func() #不能写死,赋给一个变量,index()=func
stop_time=time.time() #功能结束时间
print('run time is %s' %(stop_time-start_time))
return wrapper #一定要返回wrapper值,
@timmer #用语法实现装饰器的功能,
def index():#index的函数功能是在4秒的时间内显示出网页的内容,还想要增加新功能,就要添加新功能,就用到了装饰器
time.sleep(random.randrange(1,5))
print('welcome to index page')
index()
---结果
welcome to index page #原功能 ,
run time is 1.0044817924499512 #新增装饰器的功能,函数的运行时间
解析:没有修改源码,没有改变调用方式,增加了新功能,,
------
装饰器
由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。
>>> def now():
... print '2013-12-25'
...
>>> f = now
>>> f()
2013-12-25
函数对象有一个__name__
属性,可以拿到函数的名字:
>>> now.__name__
'now'
>>> f.__name__
'now'
现在,假设我们要增强now()
函数的功能,比如,在函数调用前后自动打印日志,但又不希望修改now()
函数的定义,这种在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)。
本质上,decorator就是一个返回函数的高阶函数。所以,我们要定义一个能打印日志的decorator,可以定义如下:
def log(func):
def wrapper(*args, **kw):
print 'call %s():' % func.__name__
return func(*args, **kw)
return wrapper
观察上面的log
,因为它是一个decorator,所以接受一个函数作为参数,并返回一个函数。我们要借助Python的@语法,把decorator置于函数的定义处:
@log
def now():
print '2013-12-25'
调用now()
函数,不仅会运行now()
函数本身,还会在运行now()
函数前打印一行日志:
>>> now()
call now():
2013-12-25
把@log
放到now()
函数的定义处,相当于执行了语句:
now = log(now)
由于log()
是一个decorator,返回一个函数,所以,原来的now()
函数仍然存在,只是现在同名的now变量指向了新的函数,于是调用now()
将执行新函数,即在log()
函数中返回的wrapper()
函数。
wrapper()
函数的参数定义是(*args, **kw)
,因此,wrapper()
函数可以接受任意参数的调用。在wrapper()
函数内,首先打印日志,再紧接着调用原始函数。
如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本:
def log(text):
def decorator(func):
def wrapper(*args, **kw):
print '%s %s():' % (text, func.__name__)
return func(*args, **kw)
return wrapper
return decorator
这个3层嵌套的decorator用法如下:
@log('execute')
def now():
print '2013-12-25'
执行结果如下:
>>> now()
execute now():
2013-12-25
和两层嵌套的decorator相比,3层嵌套的效果是这样的:
>>> now = log('execute')(now)
我们来剖析上面的语句,首先执行log('execute')
,返回的是decorator
函数,再调用返回的函数,参数是now
函数,返回值最终是wrapper
函数。
以上两种decorator的定义都没有问题,但还差最后一步。因为我们讲了函数也是对象,它有__name__
等属性,但你去看经过decorator装饰之后的函数,它们的__name__
已经从原来的'now'
变成了'wrapper'
:
>>> now.__name__
'wrapper'
因为返回的那个wrapper()
函数名字就是'wrapper'
,所以,需要把原始函数的__name__
等属性复制到wrapper()
函数中,否则,有些依赖函数签名的代码执行就会出错。
不需要编写wrapper.__name__ = func.__name__
这样的代码,Python内置的functools.wraps
就是干这个事的,所以,一个完整的decorator的写法如下:
import functools
def log(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print 'call %s():' % func.__name__
return func(*args, **kw)
return wrapper
或者针对带参数的decorator:
import functools
def log(text):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kw):
print '%s %s():' % (text, func.__name__)
return func(*args, **kw)
return wrapper
return decorator
import functools
是导入functools
模块。模块的概念稍候讲解。现在,只需记住在定义wrapper()
的前面加上@functools.wraps(func)
即可。
小结
在面向对象(OOP)的设计模式中,decorator被称为装饰模式。OOP的装饰模式需要通过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator。Python的decorator可以用函数实现,也可以用类实现。
decorator可以增强函数的功能,定义起来虽然有点复杂,但使用起来非常灵活和方便。
请编写一个decorator,能在函数调用的前后打印出'begin call'
和'end call'
的日志。
再思考一下能否写出一个@log
的decorator,使它既支持:
@log
def f():
pass
又支持:
@log('execute')
def f():
pass
============================
迭代器
1.重复 2.下次的重复基于上次的结果
模拟迭代
while True :
cmd = input('>>:')
print(cmd)# 只重复,不迭代
l=['a','b','c']
count=0
while count < len(l) :
print(l[count])
count+=1 #每次的结果都是基于上次
有序列的数据类型,都有索引
l=['a','b','c']
for count in range(len(l)) :
print(l[count]) #依赖索引求值
#没有索引取值
d={'a':1,'b':2,'c':3}
for k in d:
print(d[k]) #得到1,2,3
print(k) #得到a,b,c
Python提供一种不依赖索引的迭代方式,
给一些对象内置了__iter__方法,只要对象有__iter__称为可迭代对象
-------------------------------
s1.__iter__() #字符串是可迭代对象
l=[1,2,3]
l.__iter__() #列表是可迭代对象
t=(1,2,3)
t.__iter__() #元组是可迭代对象
set={1,2,3}
set.__iter__() #集合是可迭代对象
f=open('1.txt',encoding='utf-8')
f.__iter__() #文件是可迭代对象
d={'a':1,'b':2,'c':3}
d.__iter__() #字典是可迭代对象
--------------------------------------------
作用:执行obj.__iter__() 得到的结果就是迭代器
得到迭代器既有__iter__() 要得到值还要用 __next__()方法
d={'a':1,'b':2,'c':3}
a=d.__iter__() #字典是可迭代对象 a就是迭代器
print(a)-----结果 <dict_keyiterator object at 0x00000116709D85E8>
d={'a':1,'b':2,'c':3}
a=d.__iter__() #字典是可迭代对象
print(a.__next__())
print(a.__next__())
print(a.__next__())
结果
a
b
c
迭代器的特点:
1.提供一种不依赖于索引的求值方法
2.惰性计算,什么时间用,什么 时间调,节省内存空间
缺点:
1.取值麻烦,要依次按照顺序取值
2.迭代只能往后取,不能向前取
3.不能获取迭代的长度,
for i in list: list是经过了两步 1. list__iter__() 结果赋值给 i , #生产迭代器
2. i.__next__() 得到list的每个值
==============================================================================
关于函数作业:
一:编写函数,(函数执行的时间是随机的)
import random
import time
def timmer():
start_time=time.time()
time_sleep=random.randrange
stop_time=time.time()
res=start_time-stop_time
return res
print(res)
二:编写装饰器,为函数加上统计时间的功能,,# 就是说一个功能在一个时间段内启动到结束的时间,就限定在一个范围,
import time
import random
def timmer(func):
def wrapper():
start_time=time.time()
func()
stop_time=time.time()
res=stop_time-start_time
print('run time is %s ' %(stop_time-start_time))
return wrapper #不能条括号,加了就是是执行函数,只需要wrapper返回值就可以
@timmer
def index():
time.sleep(random.randrange(1,4))
print('welcome to index page')
index()
--结果
welcome to index page #index函数功能,
run time is 3.0003855228424072 #统计出函数运行的时
四:编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件)
,要求登录成功一次,后续的函数都无需再输入用户名和密码
注意:从文件中读出字符串形式的字典,
可以用eval('{"name":"egon","password":"123"}')转成字典格式
def auth(func):
def wrapper():
name=input('name:').strip()
pwd=input('pwd:').strip()
filepath=r'C:UserslenovoPycharmProjectsuntitled6.14file1.txt'
with open(filepath,'rb') as f :
dic_user=eval(f.read()) #eval把文件转换为字典
#for i in dic_user:
if name in dic_user or pwd == dic_user[name] :
print('ok')
func()
else:
print('err')
return wrapper
@auth
def index() :
print('welcome to index page')
index()
五:编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果
from urllib.request import urlopen
def index(url):
return urlopen(url).read()
print(index('https://www.baidu.com/'))
结果:
b'<html> <head> <script> location.replace
(location.href.replace("https://","http://")); </s
六:为题目五编写装饰器,实现缓存网页内容的功能:
# 具体:实现下载的页面存放于文件中,如果文件内有值
# (文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后存到文件中
from urllib.request import urlopen
import os
cache_file=r'C:UserslenovoPycharmProjectsuntitled6.14cache.txt'
def make_cache(func):
def wrapper(*args,**kwargs):
with open(cache_file,'rb') as f :
if os.path.getsize(cache_file) : #判断文件大小
res=f.read()
else:
res=func(*args,**kwargs) #func()就是get()函数,不管get传入什么,都能接受,
with open(cache_file,'wb') as f :# 把写的方式打开文件,
res=f.write(res) #把写的文件返回
return res
return wrapper
@make_cache
def get(url):
return urlopen(url).read()
print(get('http://www.baidu.com'))
---结果--- b'<html>
<head>
<script>
location.replace(location.href.replace("https://",........
cache.TXT文件内容
===========
# 基于上题所讲网页缓存装饰器的基础上,实现缓存不同网页的功能
# 要求,用户提交的不同url,都能缓存下来,对相同的url发起下载请求,优先从缓存里取内容
# 分析:
#1,判断网页是否在有缓存,有缓存,就从文件里读,没有缓存,就下载,
#2.没有缓存,不同的网页根据他的哈希值来判断,存到哪个文件夹
未完,待续
===========================
生成器(generator)
先定义概念,生成器就是迭代器,也有next() 方法,
函数体内包含yield的关键字,该函数的执行结果就是生成器,
iteration 迭代器
generation 生成器
yield相当于return,一次只能返回一次值,
为什么要有生成器?
通过列表生成式,我们可以直接创建一个列表。但是,受到内存限制,列表容量肯定是有限的。而且,创建一个包含100万个元素的列表,不仅占用很大的存储空间,如果我们仅仅需要访问前面几个元素,那后面绝大多数元素占用的空间都白白浪费了。
所以,如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢?这样就不必创建完整的list,从而节省大量的空间。在Python中,这种一边循环一边计算的机制,称为生成器(Generator)。
简单生成器
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:
>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x104feab40>
简单生成器
要创建一个generator,有很多种方法。第一种方法很简单,只要把一个列表生成式的[]改成(),就创建了一个generator:
yield相当于把 .__iter__()和 .__next__()为函数封装好
遵循迭代器的取值方式,obj.__next__(),触发函数的执行,函数的暂停与再继续状态都有yield保存。
用yield 生成斐波那契数据类型(100内)
def foo():
a = b = 1
yield a
yield b
while 1:
a,b = b,a+b
yield b
for num in foo():
if num > 100:
break
print(num)
foo() #1 1 2 3 5 8 13 21 34 55 89