十闭包函数函数对象装饰器

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了十闭包函数函数对象装饰器相关的知识,希望对你有一定的参考价值。

函数对象:

  函数是第一类对象

第一类对象:

  指可以在执行期创造并作为参数传递给其他函数或存入一个变量的实体     

第一类对象所特有的特性为:

  • 可以当容器被存入变量或其他结构
  • 可以被作为参数传递给其他函数
  • 可以被作为函数的返回值
  • 可以在执行期创造,而无需完全在设计期全部写出
  • 即使没有被系结至某一名称,也可以存在

函数、类、模块等所有对象都是第一类的

 


 

闭包函数:

  函数内定义的函数为内部函数

  内部函数包含对外部作用域而非全局作用域的引用

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

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

  举个栗子

def f1():
    x=1
    def f2():
        print(x)
    return f2
f = f1()
print(f)        # <function f1.<locals>.f2 at 0x00000189C2226158>
f()
print(f.c.__closure__[0].cell_contents)  查看闭包元素   闭包函数都有 __closure__ 方法
这里内部函数f2打印x,f2中没有变量x,去上一层找,找到f1中的x,然后打印 这里的 f 就是闭包函数,包含了对f1中x的引用
特点:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使该函数无论在何处调用,优先使用自己外层包裹的作用域,永远携带着一种状态
   拿到函数的内存地址,什么时候用就什么时候执行,惰性计算

闭包函数和作用域:
money = 666
def foo(name):
    print(‘%s have %s money‘ %(name,money))

def foo1():
    money = 999
    foo(‘shuai‘)
foo1()                                    #  shuai have 666 money

这里打印不会是999,函数在定义时就已经确定了作用域,所以当doo函数内没有money时,会去找全局的money

  闭包函数,让我一直有着一笔钱

money = 888
def func1():
    name = ‘shuai‘
    def func():
        money = 666
        def foo():
            print(‘%s have %s money‘ %(name,money))
        return foo
    return func
f = func1()
f1 = f()

def foo1():
    money = 999
    f1()
foo1()

包了两层
    先看func1和func,这两个函数返回闭包函数 func,包含了 name
    func 和 foo ,这俩函数返回闭包函数 foo,包含了 money

f = func1()   拿到 func,再执行f1 = f() 拿到 foo

  简单应用:


from urllib.request import urlopen
def get(url):
    return urlopen(url).read()
print(get(‘http://www.xiaohua100.cn‘))

获取校花网信息  
问题:每次都要传url,网站过多,url是没法记住的


闭包应用
from urllib.request import urlopen
def index(url):
    def get():
        return urlopen(url).read()
    return get

xiaohua = index(‘http://xiaohua100.cn‘)
print(xiaohua())
jidong = index(‘https://www.jd.com/‘)
print((jidong()))

这样想要下载哪个就执行哪个,不用就放着,并且不用传url;

  


 

装饰器

  实际就是闭包函数的

  软件开发的开发封闭原则

    对扩展是开放的,对修改是封闭的

装饰器本身可以是任意可调用对象,被装饰者也可以是任意可调用对象

装饰器的原则:1 不修改被装饰对象的源代码 
                    2 不修改被装饰对象的调用方式
	
装饰器的目标:在遵循装饰器原则前提下,为被装饰对象添加上新功能        

  通过装饰器来检测程序运行时间:

import time
import random
# 装饰器
def func(func):     # func = index
    def timmer():
        start_time = time.time()
        func()      # index()
        end_time = time.time()
        print(‘运行时间:%s‘ %(end_time-start_time))
    return timmer
#被装饰对象
def index():
    time.sleep(random.randrange(1,6))
    print(‘欢迎!‘)

index = func(index)
index()

  上面的问题:

如果还有其他的被装饰对象,那么装饰谁就得写一遍代码:
index = func(index)
index()

admin_page = func(admin_page )
admin_page ()

这样很麻烦

    @语法:

      写在在被装饰对象的正上方 :@装饰器  

      流程跟上方是一样的:将下方函数当做参数传给装饰器,把返回的结果重新赋值给下方函数的名字   

上面的代码可以这样写,省去了   index = func(index)

@func           # func(index)
def index():
    time.sleep(random.randrange(1,6))
    print(‘欢迎!‘)
index()

  举个栗子

import time
import random
def timmer(func):
    def f_time():
        start_time = time.time()
        func()
        end_time = time.time()
        print(‘运行时间 %s‘ %(end_time-start_time))
    return f_time
def func(func):
    def foo():
        name = input(‘用户名:‘)
        pwd = input(‘密码:‘)
        if name == ‘shuai‘ and pwd == ‘123‘:
            print(‘登录成功!‘)
            func()
        else:
            print(‘登录失败!‘)
    return foo
@func
@timmer        # 在下面的先执行,这样就把认证也计算在内
def index():
    # time.sleep(random.randrange(1,6))
    time.sleep(2)
    print(‘欢迎!‘)
index()
注:
  被装饰的对象,上面的装饰器,离得近的先执行

分析:
@timmer  是 index = timmer(index) 这里传入的是最原始的index,得到包含了f_time的
@func  是 index = auth(timmer(index)) 这里传入的index是@timmer执行后的index,然后再赋值给index

  

    有参装饰器:

db_path=r‘F:\python基础\a.txt‘

login_dic={
    ‘user‘:None,
    ‘status‘:False,
}
def deco(auth_type=‘170‘):
    def auth(func):
        def wrapper(*args,**kwargs):
            if auth_type == ‘170‘:
                if login_dic[‘user‘] and login_dic[‘status‘]:
                    res = func(*args, **kwargs)
                    return res
                name=input(‘用户名: ‘)
                password=input(‘密码: ‘)
                with open(db_path,‘r‘,encoding=‘utf-8‘) as f:
                    user_dic=eval(f.read())
                if name in user_dic and password == user_dic[name]:
                        print(‘登录成功‘)
                        login_dic[‘user‘]=name
                        login_dic[‘status‘]=True
                        res=func(*args,**kwargs)
                        return res
                else:
                    print(‘登录失败‘)
            elif auth_type == ‘160‘:
                print(‘长的矮的‘)
            elif auth_type == ‘180‘:
                print(‘长的高的‘)
            else:
                print(‘没有的选项‘)
        return wrapper
    return auth
@deco(auth_type=‘170‘) #@auth #index=auth(index)   这里先执行deco函数,执行的结果auth作为index的装饰器
def index():
    print(‘欢迎登录‘)
@deco(auth_type=‘160‘)
def home(name):
    print(‘欢迎%s登录‘ %name)
index()
home(‘shuai‘)



这里是简单例子,根据身高来执行不同的操作,这个身高是一个条件,使用闭包把这个变量包进去,在使用装饰器deco时,需要传入这个值,这时deco执行,返回autn,所以装饰器就变成了@auth,然后就像无参装饰器一样运行了

  

练练

(1)

tag = True
while tag:
    name = input(‘用户名:‘)
    pwd = input(‘密码:‘)
    f = open(‘a.txt‘,encoding=‘UTF-8‘)
    for line in f:
        s = eval(line)
        # print(type(s))
        if name == s[‘name‘] and pwd == s[‘password‘]:
            print(‘登录成功!‘)
            tag = False
            break
        print(‘登录失败!‘)

  

(2)

from urllib.request import urlopen
import os
cache_path=r‘F:\下载\cache.txt‘
def make_cache(func):
    def wrapper(*args,**kwargs):
        if os.path.getsize(cache_path):
            #有缓存
            print(‘\033[45m=========>有缓存\033[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()
get(‘https://www.baidu.com/‘)

  

 

以上是关于十闭包函数函数对象装饰器的主要内容,如果未能解决你的问题,请参考以下文章

python学习第十二天:闭包函数与装饰器

python闭包函数装饰器

python 闭包装饰器

装饰器

Python之闭包装饰器

CSIC_716_20191112闭包函数和装饰器