python--闭包函数、装饰器

先来点补充。

x=1
def foo():
    print(x)
x=2

foo()
结果:
2
x=1
def foo():
    global x
    x=10000000000000
    print(x)

foo()
print(x)
结果:
10000000000000
10000000000000
x=1
def f1():
    x=2
    def f2():
        x=3
        def f3():
            # global x
            nonlocal x
            x=1000000000
        f3()
        print('f2内部的打印',x)
    f2()
    print('f1内部的打印', x)

f1()
print(x)
结果:
f2内部的打印 1000000000
f1内部的打印 2
1
def foo():
    x = 1
    def wrapper():
        nonlocal x
        x = 1000
        print(x)
    return wrapper

f = foo()
print(f.__closure__)
结果:
(<cell at 0x0000000002147498: int object at 0x00000000601CB440>,)

闭包函数

函数内部定义的函数,称为内部函数,该内部函数包含对外部作用域,而不是对全局作用域名字的引用,那么该内部函数称为闭包函数

name='alex'
def func():
    name='egon'
    def bar():
        print(name)
    return bar

b=func()
print(b)
结果:
<function func.<locals>.bar at 0x0000000001E90730>
name='alex'
def func():
    name='egon'
    x=1000000000000000000000
    def bar():
        print(name)
        print(x)
        a=1
        b=2
    return bar
#
f=func()

print(f.__closure__[0].cell_contents)
print(f.__closure__[1].cell_contents)
结果:
egon
1000000000000000000000
name='alex'
def func():
    name='egon'
    x=1000000000000000000000
    def bar():
        print(name)
        print(x)
        a=1
        b=2
    print(bar.__closure__)

func()
结果:
(<cell at 0x0000000002167498: str object at 0x00000000021EE030>, <cell at 0x00000000021674C8: int object at 0x00000000021F7350>)

闭包函数的特点:

    • 自带作用域
    • 延迟计算
name='alex'
def func():
    def bar():
        print(name)
    return bar

f=func()
print(f.__closure__)

f()
结果:
None
alex
money=1000
def tell_ino(name):
    print('%s have money %s' %(name,money))

tell_ino('egon')

def f1():
    money=10
    tell_ino('egon')

f1()
结果:
egon have money 1000
egon have money 1000
money=1000
def f1():
    money=10
    def tell_ino(name):
        print('%s have money %s' %(name,money))
    tell_ino('egon')

f1()
结果:
egon have money 10

#包一层

def wrapper():
    money=1000
    def tell_info():
        print('egon have money %s' % money)
    return tell_info
tell_info
=wrapper() print(tell_info) def foo(): money=100 tell_info() foo() 结果: <function wrapper.<locals>.tell_info at 0x00000000023F0730> egon have money 1000

#包两层

def aaa():
    name = 'egon'
    def wrapper():
        money = 1000
        def tell_info():
            print('egon have money %s' % money)
            print('my name is %s' % name)
        return tell_info
    return wrapper

wrapper = aaa()
tell_info = wrapper()
print(tell_info.__closure__[0].cell_contents)
print(tell_info.__closure__[1].cell_contents)
结果:
1000
egon


我们可以看到,wrapper中引用了外围作用域变量name,但name信息存在于wrapper的定义之外。我们称namewrapper环境变量

同样的,tell_info中引用了外围作用域变量moneymoney信息存在于tell_info的定义之外。我们称moneytell_info环境变量;一个函数和它的环境变量合在一起,就构成了一个闭包(closure)。
在Python中,所谓的闭包是一个含有环境变量的函数对象。环境变量被保存在函数对象的__closure__属性中。

'''

def tell_info(name):
    print('%s have money %s' % (name,money))

def foo():
    money = 100
    tell_info('egon')

foo()
结果:
NameError: name 'money' is not defined

报错NameError: name 'money' is not defined

原因:
    函数的作用域关系在函数定义阶段就已经固定,与调用位置无关
    无论函数在何处调用,都需要回到定义阶段去找对应的作用域关系
    此例:虽然tell_info('egon')是在foo内调用并且引用money,但仍需要回到定义
    tell_info的阶段去找作用域关系,而定义时tell_info引用的money就是全局的money
    如果全局不存在则抛出异常NameError

'''

#定义闭包函数的基本形式:

 def 外部函数名():
     内部函数需要的变量
     def 内部函数():
         引用外部变量
     return 内部函数

def deco():
    x = 1
    def wrapper():
        print(x)

    return wrapper

wrapper = deco()

print(wrapper)
结果:
<function deco.<locals>.wrapper at 0x00000000027B0730>
def deco1():
    y = 2
    def deco():
        x = 1
        def wrapper():
            print(x)
            print(y)

        return wrapper
    return deco

deco = deco1()
wrapper = deco()

wrapper()
结果:
1
2
from urllib.request import urlopen

print(urlopen('http://www.xiaohua100.cn/').read())
print(urlopen('https://www.python.org').read())

def get(url):
    return urlopen(url).read()

print(get('http://www.xiaohua100.cn/'))
结果:
b'xefxbbxbf<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">...
b'<!doctype html>
<!--[if lt IE 7]>   <html class="no-js ie6 lt-ie7 lt-ie8 lt-ie9">   <![endif]-->
<!--[if IE 7]>      <html class="no-js ie7 lt-ie8 lt-ie9">...
b'xefxbbxbf<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">...
from urllib.request import urlopen
def index(url):
    # url='http://www.xiaohua100.cn/'
    def get():
        return urlopen(url).read()
    return get

xiaohua=index('http://www.xiaohua100.cn/')

python=index('https://www.python.org')

baidu=index('http://www.baidu.com')

print(python())
结果:
b'<!doctype html>
<!--[if lt IE 7]>   <html class="no-js ie6 lt-ie7 lt-ie8 lt-ie9">   <![endif]-->
<!--[if IE 7]>      <html class="no-js ie7 lt-ie8 l
t-ie9">...

装饰器

'''
一:开放封闭原则,对扩展是开放的,对修改是封闭的

二:装饰器,装饰器本质可以是任意可调用对象,被装饰的对象也可以是任意
    可调用对象,
    装饰器的功能是:
        在不修改被装饰对象源代码以及调用方式的前提下为期添加新功能

        原则:
            1.不修改源代码
            2.不修改调用方法
        目标:添加新功能
'''

import time
import random
#装饰器
def timmer(func):
    # func=index
    def wrapper():
        start_time = time.time()
        func() #index()
        stop_time = time.time()
        print('run time is %s' % (stop_time-start_time))
    return wrapper
#被装饰函数
def index():
    time.sleep(random.randrange(1, 5))
    print('welcome to index page')

def home():
    time.sleep(random.randrange(1, 3))
    print('welcome to HOME page')

index = timmer(index) #index=wrapper
home = timmer(home)

index() #wrapper()
home()
结果:
welcome to index page
run time is 1.0000572204589844
welcome to HOME page
run time is 1.0000572204589844

#装饰器的语法:在被装饰对象的正上方的单独一行,@装饰器名字

import time
import random
#装饰器
def timmer(func):
    def wrapper():
        start_time = time.time()
        func()
        stop_time = time.time()
        print('run time is %s' % (stop_time-start_time))
    return wrapper
#被装饰函数
@timmer #index=timmer(index)
def index():
    time.sleep(random.randrange(1, 5))
    print('welcome to index page')
@timmer #home=timmer(home)
def home():
    time.sleep(random.randrange(1, 3))
    print('welcome to HOME page')

index() #wrapper()
home()
结果:
welcome to index page
run time is 4.0002288818359375
welcome to HOME page
run time is 1.0000574588775635

#加多个装饰器

定义阶段外部函数的执行顺序是自下而上
调用阶段内部函数的执行顺序是自上而下

import time
import random
#装饰器
def timmer(func):
    def wrapper():
        start_time = time.time()
        func()
        stop_time = time.time()
        print('run time is %s' % (stop_time-start_time))
    return wrapper
def auth(func):
    def deco():
        name = input('name: ')
        password = input('password: ')
        if name == 'egon' and password == '123':
            print('login successful')
            func() #wrapper()
        else:
            print('login err')
    return deco

#被装饰函数
@auth #index=auth(wrapper) #index=deco                      #index=auth(wrapper) #index=deco
@timmer #index=timmer(index) #index=wrapper
def index():
    # time.sleep(random.randrange(1,5))
    time.sleep(3)
    print('welcome to index page')

@timmer
@auth
def home():
    time.sleep(random.randrange(1, 3))
    print('welcome to HOME page')

index() #deco()
home()
结果:
name: egon
password: 123
login successful
welcome to index page
run time is 3.000171661376953
name: egon
password: 123
login successful
welcome to HOME page
run time is 5.638322591781616

#装饰器修订

import time
import random
#装饰器
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
def index():
    time.sleep(random.randrange(1, 5))
    print('welcome to index page')
@timmer
def home(name):
    time.sleep(random.randrange(1, 3))
    print('welcome to %s HOME page' % name)
    return 123123123123123123123123123123123123123123

res1 = index()
print('index return %s' % res1)
res2 = home('egon') #wraper()
print('home return %s' % res2)
结果:
welcome to index page
run time is 1.0000572204589844
index return None
welcome to egon HOME page
run time is 2.0001142024993896
home return 123123123123123123123123123123123123123123
作业:
一:编写函数,(函数执行的时间是随机的)
二:编写装饰器,为函数加上统计时间的功能
三:编写装饰器,为函数加上认证的功能

四:编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
    注意:从文件中读出字符串形式的字典,可以用eval('{"name":"egon","password":"123"}')转成字典格式

五:编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果

六:为题目五编写装饰器,实现缓存网页内容的功能:
    具体:实现下载的页面存放于文件中,如果文件内有值(文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后存到文件中

七:还记得我们用函数对象的概念,制作一个函数字典的操作吗,来来来,我们有更高大上的做法,在文件开头声明一个空字典,然后在每个函数前加上装饰器,完成自动添加到字典的操作
答案:
# 一:编写函数,(函数执行的时间是随机的)
# import time
# import random
# def index():
#     time.sleep(random.randrange(1,5))
#     print('welcome to index page')
#
# index()

# 二:编写装饰器,为函数加上统计时间的功能
# import time
# import random
# def timmer(func):
#     # func=index
#     def wrapper():
#         start_time = time.time()
#         func() #index()
#         stop_time = time.time()
#         print('running time is %s'%(stop_time -start_time ))
#     return wrapper
#
# @timmer
# def index():
#     time.sleep(random.randrange(1,5))
#     print('welcome to index page')
#
# index()


# 三:编写装饰器,为函数加上认证的功能
# import time
# import random
# def auth(func):
#     #func=register
#     def wrapper():
#         username=input('name: ')
#         password=input('password: ')
#         if username =='kaylee' and password =='123':
#             print('login successful')
#             func()
#         else:
#             print('login error')
#     return wrapper
#
# @auth
# def register():
#     time.sleep(random.randrange(1,5))
#     print('welcome to register page')
#
# register()


# 四:编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码
#     注意:从文件中读出字符串形式的字典,可以用eval('{"name":"egon","password":"123"}')转成字典格式
# user_dic={'egon':'123',
#           'kaylee':'234',
#           'lily':'345'}
# with open('decorate2.txt','w',encoding='utf-8') as f:
#     f.write(str(user_dic))

# decorate2_path=r'C:UsersAdministratorPycharmProjectsuntitleddecorate2.txt'
# # with open('decorate2.txt','r',encoding='utf-8') as f:
# #     res=f.read()
# #     user_dic=eval(res)
#
# login_dic={'user':None,
#            'status':False}
# def auth(func):
#     def wrapper(*args,**kwargs):
#         if login_dic['user'] and login_dic['status']:
#             res = func(*args, **kwargs)
#             return res
#         name=input('name: ')
#         psw=input('password: ')
#         with open(decorate2_path,'r',encoding='utf-8') as f:
#             user_dic=eval(f.read())
#
#         if name in user_dic and psw == user_dic[name]:
#             print('login ok')
#             login_dic['user']=name
#             login_dic['status']=True
#             res=func(*args,**kwargs)
#             return res
#         else:
#             print('login error')
#
#     return wrapper
#
# @auth
# def index():
#     print('welcome to index')
# @auth
# def home(name):
#     print('welcome to %s home page'%name)
#
# index()
# home('egon')



# 五:编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果
# from urllib.request import urlopen
#
# def get(url):
#         return urlopen(url).read()
#
# print(get('http://www.baidu.com'))



# 六:为题目五编写装饰器,实现缓存网页内容的功能:
#     具体:实现下载的页面存放于文件中,如果文件内有值(文件大小不为0),就优先从文件中读取网页内容,否则,就去下载,然后存到文件中
# from urllib.request import urlopen
# import os
#
# cache_path=r'C:UsersAdministratorPycharmProjectsuntitledcache.txt'
# def make_cache(func):
#     def wrapper(*args,**kwargs):
#         if os.path.getsize(cache_path):
#             print('33[45m========>有缓存33[0m')
#             with open(cache_path,'rb') as f:
#                 res=f.read()
#         else:
#             res=func(*args,**kwargs)
#             with open(cache_path,'wb') as f:
#                 f.write(res)
#         return res
#     return wrapper
#
# @make_cache
# def get(url):
#     return urlopen(url).read()
#
# print('=========>first')
# print(get('https://www.python.org'))
# print('=========>second')
# print(get('https://www.python.org'))
# print('=========>third')
# print(get('https://www.python.org'))


#七:还记得我们用函数对象的概念,制作一个函数字典的操作吗,来来来,我们有更高大上的做法,在文件开头声明一个空字典,然后在每个函数前加上装饰器,完成自动添加到字典的操作
func_dic={}
def deco(key):
    def deco2(func):
        func_dic[key]=func
    return deco2

@deco('f1')
def f1():
    print('from f1')

@deco('f2')
def f2():
    print('from f2')

@deco('f3')
def f3():
    print('from f3')

print(func_dic )

while True:
    cmd=input('>>: ').strip()
    if cmd in func_dic:
        func_dic[cmd]()
原文地址:https://www.cnblogs.com/metianzing/p/7009616.html