• 周五. 12月 2nd, 2022

5G编程聚合网

5G时代下一个聚合的编程学习网

热门标签

闭包函数,装饰器,迭代器,生成器,yield,

admin

11月 28, 2021

闭包函数

包含对外的作用域,在内部定义函数,引用自身函数外的函数,

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()函数赋给wrapperwrapper就有了全局作用域
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 #新增装饰器的功能,函数的运行时间

解析:没有修改源码,没有改变调用方式,增加了新功能,,

------

装饰器


阅读: 229372



由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。

>>> 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








 
 
 










 

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注