python装饰器(第一对象,函数名的应用,闭包)

第一对象
在 Python 中万物皆为对象,函数也不例外,函数作为对象可以赋值给一个变量、可以作为元素添加到集合对象中、可作为参数值传递给其它函数,还可以当做函数的返回值,这些特性就是第一类对象所特有的。

先来看一个简单的例子

 def foo(text):
  return len(text) ...
foo("zen of python") 

这是一个再简单不过的函数,用于计算参数 text 的长度,调用函数就是函数名后面跟一个括号,再附带一个参数,返回值是一个整数。

函数身为一个对象,拥有对象模型的三个通用属性:id、类型、和值。

>>> id(foo) 4361313816 >>> type(foo) <class 'function'> >>> foo <function foo at 0x103f45e18>

作为对象,函数可以赋值给一个变量

>>> bar = foo
赋值给另外一个变量时,函数并不会被调用,仅仅是在函数对象上绑定一个新的名字而已。

>>> bar("zen of python") 13 >>>

同理,你还可以把该函数赋值给更多的变量,唯一变化的是该函数对象的引用计数不断地增加,本质上这些变量最终指向的都是同一个函数对象。
函数名的应用
def func():
print(666)
print(func)#直接打印函数名,得到的是函数的内存地址。<function func at 0x0000021A4F9612F0>

2.函数名可以赋值运算
def func():
print(666)
f=func
f()

3.函数名可以作为函数的参数
def func():
print(666)

def func1(x):
x()
print(999)
func1(func)

4.函数名可以作为容器类数据类型的元素
def func1():
print(111)

def func2():
print(222)

def func3():
print(333)

# l=[func1,func2,func3]
# for i in l:
# i()

dic={1:func1,2:func2,3:func3}#创建字典时,变量不用加引号''
for i in dic:
dic[i]()

5.函数名可以当函数的返回值
def func1():
print(333)

def func2(x):
print(666)
return x
ret=func2(func1)
ret()

闭包
内层函数对外层函数非全局变量的引用叫作闭包
判断是不是闭包__closure__
返回None不是闭包,返回call....是闭包
闭包有什么用
当执行一个函数时,如果解释器判断一个函数内部有闭包存在,python 就有一个机制,
此闭包所存在的临时命名空间不会随着函数的执行完毕而关闭
def func1():
name = '张山'

def inner():
print(name)
inner()
print(inner.__closure__) # <cell at 0x000000000282A768: str object at 0x0000000002856E10>

func1()

def func():
name='李四'
def inner():
nonlocal name
name='alex'
print(name)
inner()
print(inner.__closure__)#是闭包
print(name)
func()

def func():
print(999)
def inner(x):
print(666)
return x
ret=inner(func)
print(inner.__closure__)#None 不是闭包
ret()

def func1():
global name
name = '张三'

def inner():
print(name)

inner()
print(inner.__closure__) # None
func1()

def func1(x):   #x='王五' 是赋值运算
    def inner():
        print(x)    # 此时的王五是局部变量。
    inner()
    print(inner.__closure__)#(<cell at 0x00000211B5040B58: str object at 0x00000211B55C1440>,)

name = '王五'
func1(name)

def func():   #多看看
    def func1():
        name ="aa" 
    def func2():
        nonlocal name
        name = "bb" 
    def func3():
        global name
        name = "cc" 
    name = "dd" 

    func1()
    print(name)  # 1 dd
    func2()
    print(name)  # 2,bb
    func3()
    print(name)  # 3,bb 
func()
print(name)  # 4,cc


from urllib.request import urlopen

def index():
    url = "http://www.xiaohua100.cn/index.html"
    def get():
        return urlopen(url).read()
    return get

xiaohua = index()
content = xiaohua()

print(content)

装饰器

装饰器本质上就是一个python函数,他可以让其他函数在不需要做任何代码变动的前提下,增加额外的功能,装饰器的返回值也是一个函数对象。

装饰器的应用场景:比如插入日志,性能测试,事务处理,缓存等等场景

mport time
'''第一版本,测试函数low'''
def login():
    time.sleep(0.3)
    print('洗洗更健康...')

def timmer():
    start_time = time.time()
    login()
    end_time = time.time()
    print('此函数的执行时间%s' % (end_time - start_time))
timmer()


改变了我原来执行函数的执行方式,不好
def login():
    time.sleep(0.3)
    print('洗洗更健康...')
# login()

def register():
    time.sleep(0.4)
    print('洗洗更健康22222...')
# register()
def timmer(f):
    start_time = time.time()
    f()
    end_time = time.time()
    print('此函数的执行时间%s' % (end_time - start_time))

timmer(login)
timmer(register)

虽然执行函数的方式已经无限接近于原方式,但是更麻烦了,增加了两步代码。改
def login():
    time.sleep(0.3)
    print('洗洗更健康...')
# login()

def timmer(f):
    start_time = time.time()
    f()
    end_time = time.time()
    print('此函数的执行时间%s' % (end_time - start_time))

f1 = login  # 将login函数名给了f1
login = timmer  # 将timmer函数名给了login
login(f1)  # timmer(login)

初级装饰器
def login():
    time.sleep(0.3)
    print('洗洗更健康...')
# login()

def timmer(f):  # f = login函数名

    def inner():
        start_time = time.time()
        f()  # login()
        end_time = time.time()
        print('此函数的执行时间%s' % (end_time - start_time))
    return inner

login = timmer(login)  # inner 此login是新变量
login()  # inner()
简单版装饰器  语法糖
def timmer(f):  # f = login函数名
    def inner():
        start_time = time.time()
        f()  # login()
        end_time = time.time()
        print('此函数的执行时间%s' % (end_time - start_time))
    return inner

@timmer  # login = timmer(login)  # inner 此login是新变量
def login():
    time.sleep(0.3)
    print('洗洗更健康...')
login()

@timmer  # register = timmer(register)
def register():
    time.sleep(0.2)
    print('洗洗更健康22...')

login()  # inner()
被装饰的函数带参数的装饰器
def timmer(f):  # f = login函数名
    def inner(*args,**kwargs):  # args (2, 3)
        start_time = time.time()
        f(*args,**kwargs)  # login() *(2, 3) 2,3
        end_time = time.time()
        print('此函数的执行时间%s' % (end_time - start_time))
    return inner

@timmer  # login = timmer(login)  # inner 此login是新变量
def login(a,b):
    print(a,b)
    time.sleep(0.3)
    print('洗洗更健康...')

login(2,3)  # inner(2,3)

@timmer  # register = timmer(register)
def register(a):
    time.sleep(0.2)
    print('洗洗更健康22...')

register(1)  # inner(1)

def func1(x):
    x = 0
    print(x)
func1(0)
函数带返回值的装饰器 (万能装饰器)
import time
def timmer(f):  # f = login函数名
    def inner(*args,**kwargs):  # args (2, 3)
        start_time = time.time()
        ret = f(*args,**kwargs)  # login() *(2, 3) 2,3
        end_time = time.time()
        print('此函数的执行时间%s' % (end_time - start_time))
        return ret
    return inner

@timmer  # login = timmer(login)  # inner 此login是新变量
def login(a,b):
    print(a,b)
    time.sleep(0.3)
    print('洗洗更健康...')
    return 666

print(login(2,3))  # inner(2,3)
万能装饰器模板
def wrapper(f):
    def inner(*args,**kwargs):
        '''执行被装饰函数之前的操作'''
        ret = f(*args,**kwargs)
        """执行被装饰函数之后的操作"""
        return ret
    return inner
装饰器功能在不改变原函数的基础上,为原函数增加一些额外的功能,log,登录注册,等等.

在不改变原函数执行的情况下,为函数增加额外的功能。
import time
def timer(func):
    def inner():
        start = time.time()
        func()#func=func1
        print(time.time() - start)
    return inner

@timer   #==> func1 = timer(func1)
def func1():
    print('in func1')
func1()

import time
def func1():
    print('你有病啊,领导,测我的执行效率干甚?')

def timmer(f):#f=func1
    def inner():
        start_time = time.time()
        f()#f=func1
        time.sleep(0.3)
        end_time=time.time()
        print('此函数的执行效率%s' % (end_time-start_time))
    return inner()
func1=timmer(func1)
func1()

import time
def timmer(func):
    def inner(*args,**kwargs):
        start_time=time.time()
        ret=func(*args,**kwargs)
        time.sleep(0.5)
        end_time=time.time()
        print('执行时间为%s' % (end_time-start_time))
        return set
    return inner
@timmer
# login=timmer(login)
def login():
    print('hello,everyone,let''s go shopping')
login()

tast

1、整理装饰器的形成过程,背诵装饰器的固定格式
import time
def timmer(func):#这个形参随意设置
    def inner(*args,**kwargs):
        start_time=time.time()
        time.sleep(0.3)
        ret=func(*args,**kwargs)#和上面的func(形参)保持一致,相当于login
        end_time=time.time()
        print('此函数执行时间是%s' % (end_time-start_time))
        return ret
    return inner

@timmer #login=timmer(login)
def login(*args,**kwargs):
    print(args)
    print(kwargs)
func(10,11,name='alex')
万能带参数装饰器
def wrapper():
    def inner(*args,**kwargs):
         '''执行被装饰函数之前的操作'''
        ret=func(*args,**kwargs):
         '''执行被装饰函数之后的操作'''
        retturn ret
    return inner
2、编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码

'''
文件存储你的用户名,密码
'''
dic = {
    'username':None,
    'status':False,
}
def wrapper(func):
    def inner(*args,**kwargs):
        '''被装饰函数执行之前的操作'''

        if dic['status']:#?
            ret = func(*args, **kwargs)
            return ret
        else:
            i = 1
            while i < 4:
                username = input('请输入你的账号:').strip()
                password = input('请输入你的密码:').strip()
                with open('register',encoding='utf-8')as f1:
                    for line in f1:
                        line=line.strip().split()

                        if username == line[0] and password == line[1]:
                            dic['username']=line[0]
                            dic['status']=True
                            print('恭喜你登录成功')
                            ret = func(*args, **kwargs)
                            return ret
                        else:
                            print('您输入的账号或者密码错误,还剩余%s次机会' % (3-i))
                        i += 1
    return inner


@wrapper
def article():
    print('欢迎登录文章页面')

@wrapper
def dariy():
    print('欢迎登录日记页面')

@wrapper
def comment():
    print('欢迎登录评论页面')

article()
dariy()
comment()
3、编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果(升级题)
3.1.为题目3编写装饰器,实现缓存网页内容的功能:(升级题)
具体:实现下载的页面存放于文件中,如果网页有对应的缓存文件,就优先从文件中读取网页内容,否则,就去下载,然后存到文件中
4.
写函数,返回一个扑克牌列表,里面有52项,每一项是一个元组
例如:[(‘红心’,2), (‘草花’,2), …(‘黑桃’,‘A’)]
def func4(l1):
    l2=[]
    for i in l1:
        for j in range(1,14):
           l2.append((i,j))
    l2[::13]= [('红桃', 'A'),('方片', 'A'),('黑桃', 'A'),('草花', 'A')]
    l2[10::13]=  [('红桃', 'J'),('方片', 'J'),('黑桃', 'J'),('草花', 'J')]
    l2[11::13] = [('红桃', 'Q'),('方片', 'Q'),('黑桃', 'Q'),('草花', 'Q')]
    l2[12::13] = [('红桃', 'K'),('方片', 'K'),('黑桃', 'K'), ('草花', 'K')]
    return l2
l1=['红桃','方片','黑桃','草花']
print(func4(l1))
5.
写函数,传入n个数,返回字典
{‘max’:最大值,’min’:最小值}
例如: min_max(2, 5, 7, 8, 4)
返回: {‘max’:8,’min’:2}
def func5(l1):
    max(l1)
    min(l1)
    return {'max':max(l1),'min':min(l1)}
l1=[1,2,3,4,5,6]
print(func5(l1))

def func5(*args):
    max(args)
    min(args)
    print(args)#(1, 2, 3, 4, 5, 6)
    print(*args)#1 2 3 4 5 6
    return {'max':max(args),'min':min(args)}
print(func5(1,2,3,4,5,6))

def func5(*args): return {'max':max(args),'min':min(args)}
print(func5(1,5,6,789,456))
6.
写函数,专门计算图形的面积
其中嵌套函数,计算圆的面积,正方形的面积和长方形的面积
调用函数area(‘圆形’, 圆半径) 返回圆的面积
调用函数area(‘正方形’, 边长) 返回正方形的面积
调用函数area(‘长方形’, 长,宽) 返回长方形的面积
from math import pi
def area(*args):
    if args[0]=='圆形':
        def cricle():
            cricle_area= pi*args[1]*args[1]
            return cricle_area
        return cricle()
    elif args[0]=='正方形':
        def aera_square():
            aera_square=args[1]*args[1]
            return aera_square
        return aera_square()
    else:
        def area_rectangle():
            area_rectangle=args[1]*args[2]
            return area_rectangle
        return area_rectangle()
print(area('圆形',2))
print(area('正方形',3))
print(area('长方形',3,6))
7.
写函数,传入一个参数n,返回n的阶乘
例如: cal(7)
计算7 * 6 * 5 * 4 * 3 * 2 * 1
def cal(n):
    factorial=1
    for i in range(n,1,-1):
        factorial=factorial*i
    return factorial
print(cal(8))
8、用面向函数的思想完成购物车(升级题)
函数一:实现三次登陆功能
函数二:实现新用户注册功能
函数三:购物功能
进阶任务:将购物功能拆分成多个函数

函数一:实现三次登陆功能
def login():
    flag=True
    while flag:
        i=1
        while i<4:
            username=input('请输入你的账号:').strip()
            password=input('请输入你的密码:').strip()
            with open ('login',encoding='utf-8') as f1:
                for line in f1:
                    line=line.strip().split()
                    if username==line[0] and password==line[1]:
                        print('恭喜你登录成功')
                        i=4
                        flag = False

                    else:
                        print('你输入的账号或者密码错误,还有%s次机会' % (3-i))
                    i+=1
login()
函数二:实现新用户注册功能
def register():
    username=input('请输入你的账号:').strip()
    password=input('请输入你的密码:').strip()
    with open('register',encoding='utf-8') as f1:
        for line in f1:
            line=line.strip().split()
            if username not in line[0]:
                username = input('该账号不存在,请先注册账号:').strip()
                password = input('请输入你的密码:').strip()
                if username not in line[0]:
                    password1 = input('请再次输入你的密码:').strip()
                    if password1==password:
                        print('恭喜你注册成功')
                        with open('register',encoding='utf-8',mode='a') as f1:
                            f1.write( '\t' + username+'\t' + password)
                            # f1.write('\n'+'\t' + username + '\t' + password)#能执行,但是报错
                    else:
                        password1 = input('您两次输入的密码不一致,请再次输入你的密码:').strip()
                else:
                    print('该账号已存在,请重新登录')

register()
函数三:购物功能
'''普通版购物车'''
goods = [{"name": "电脑", "price": 1999},
         {"name": "鼠标", "price": 10},
         {"name": "游艇", "price": 20},
         {"name": "美女", "price": 998},
]
#
shopping_car = []
'''
shopping_car = {
    0:{"name": "电脑", "price": 1999,'amount':0},
    1:{"name": "鼠标", "price": 10,'amount':0}
}
'''
flag = True
while flag:
    assets = input('请输入你的总资产:').strip()
    if assets.isdigit():
        assets = int(assets)
        print('*****商品展示*****')
        for num, goods_dic in enumerate(goods,1):
            print("{}\t{}\t\t{}".format(num, goods_dic['name'], goods_dic['price']))
        print('******************')
        while True:
            goods_num = input('请选择您要购买的商品序号/q或者Q结算:').strip()
            if goods_num.isdigit():
                goods_num = int(goods_num)
                if 0 < goods_num <= len(goods):

                    if assets >= goods[goods_num - 1]['price']:
                        assets -= goods[goods_num - 1]['price']
                        print('您已经成功购买了%s商品' % goods[goods_num - 1]['name'])
                        shopping_car.append(goods[goods_num - 1]['name'])
                    else:
                        print('您的余额不足,还剩%d钱,请及时充值' % assets)

                else:
                    print('您输入的序号超出范围,请重新输入')
            elif goods_num.upper() == 'Q':
                flag = False
                print('您已成功购买如下商品:')
                for i in shopping_car:
                    print(i)
                break

            else:
                print('您输入的有非数字,请重新输入')
    else:
        print('您输入的有非数字,请重新输入')
l1 = ['aaa', 'bbb', 'ccc']
for i in enumerate(goods):
print(i)
for i in enumerate(l1,100):
print(i)
#升级版购物车
goods = [{"name": "电脑", "price": 1999},
         {"name": "鼠标", "price": 10},
         {"name": "游艇", "price": 20},
         {"name": "美女", "price": 998},
]

shopping_car = {}
'''
shopping_car = {
    0:{"name": "电脑", "price": 1999,'amount':0},
    1:{"name": "鼠标", "price": 10,'amount':0}
}
'''
flag = True
while flag:
    assets = input('\033[1;35;0m请输入你的总资产:\033[0m').strip()
    if assets.isdigit():
        assets = int(assets)
        origal_assets = assets
        print('*****商品展示*****')
        for num, goods_dic in enumerate(goods,1):
            print("{}\t{}\t\t{}".format(num, goods_dic['name'], goods_dic['price']))
        print('******************')
        while True:
            goods_num = input('请选择您要购买的商品序号/q或者Q结算:').strip()
            if goods_num.isdigit():
                goods_num = int(goods_num)
                if 0 < goods_num <= len(goods):

                    if assets >= goods[goods_num - 1]['price']:
                        assets -= goods[goods_num - 1]['price']
                        print('您已经成功购买了%s商品' % goods[goods_num - 1]['name'])
                        if (goods_num - 1) in shopping_car:
                            shopping_car[goods_num - 1]['amount'] += 1
                        else:
                            shopping_car[goods_num - 1] = {
                                "name": goods[goods_num - 1]['name'],
                                "price": goods[goods_num - 1]['price'],
                                'amount': 1,
                            }
                    else:
                        print('您的余额不足,还剩%d钱,请及时充值' % assets)

                else:
                    print('您输入的序号超出范围,请重新输入')
            elif goods_num.upper() == 'Q':
                flag = False
                print('您已成功购买如下商品:')
                for i in shopping_car:
                    print('序号:{},商品名称:{},商品单价:{},购买数量:{}'.\
                          format(i+1,
                                 shopping_car[i]['name'],
                                 shopping_car[i]['price'],
                                 shopping_car[i]['amount']))
                print('此次购物消费%s元,还剩%s元' % (origal_assets-assets, assets))
                break

            else:
                print('您输入的有非数字,请重新输入')
    else:
        print('您输入的有非数字,请重新输入')


print('\033[1;35;0m字体变色,但无背景色 \033[0m')  # 有高亮 或者 print('\033[1;35m字体有色,但无背景色 \033[0m')
print('\033[1;45m 字体不变色,有背景色 \033[0m')  # 有高亮
print('\033[1;35;46m 字体有色,且有背景色 \033[0m')  # 有高亮
print('\033[0;35;46m 字体有色,且有背景色 \033[0m')  # 无高亮
9,在编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码。
这个作业之上进行升级操作:
设置两套密码,一套为微信账号密码,一套为qq账号密码保存在文件中。
设置四个函数,分别代表
京东首页,京东超市,淘宝首页,淘宝超市。
循环打印四个选项:东首页,京东超市,淘宝首页,淘宝超市。
供用户选择,用户输入选项后,执行该函数,四个函数都加上认证功能,只要登陆成功一次,在选择其他函数,后续都无需输入用户名和密码。
相关提示:用带参数的装饰器。装饰器内部加入判断,验证不同的账户密码。
dic = {
    'username':None,
    'status':False,
}


def login(flag):
    def wrapper(func):
        def inner(*args, **kwargs):
            if dic['status']:
                ret = func(*args, **kwargs)
                return ret
            else:
                i = 0
                while i < 3:
                    username = input('请输入用户名(用%s账号):' % flag).strip()
                    password = input('请输入密码:').strip()
                    with open('user_pwd',encoding='utf-8') as f1:
                        msg_dic = eval(f1.readline())
                        # {'微信': {'password': '123', 'username': '老男孩'}, 'qq': {'password': '123', 'username': '老男孩1'}}
                        if username == msg_dic[flag]['username'] and password == msg_dic[flag]['password']:
                            dic['username'] = username
                            dic['status'] = True
                            ret = func(*args, **kwargs)
                            return ret
                        else:
                            print('您输入的用户或者密码错误,请重新输入,还有%s次机会' % (2-i))
                            i += 1
        return inner
    return wrapper




@login('微信')
def taobao_home():
    print('淘宝首页')

@login('微信')
def taobao_shop():
    print('淘宝超市')

@login('qq')
def jingdong_home():
    print('京东首页')

@login('qq')
def jingdong_shop():
    print('京东超市')

choice_dict = {
    1: taobao_home,
    2: taobao_shop,
    3: jingdong_home,
    4: jingdong_shop,
}

while True:
    print('1 淘宝首页\n2 淘宝超市\n3 京东首页\n4 京东超市')
    choice_num = input('请选择输入的序号:').strip()
    if choice_num.isdigit():
        choice_num = int(choice_num)
        if 0 < choice_num <= len(choice_dict):
            choice_dict[choice_num]()
        else:
            print('请输入范围内的序号')
    else:
        print('您输入的有非法字符,请重新输入')
10给每个函数写一个记录日志的功能,
功能要求:每一次调用函数之前,要将函数名称,时间节点记录到log的日志中。
所需模块:
import time

def wrapper(func):
    def inner(*args, **kwargs):
        struct_time = time.localtime()
        time_now = time.strftime("%Y-%m-%d %H:%M:%S", struct_time)
        with open('log', encoding='utf-8', mode='a') as f1:
            f1.write('在%s是时间,执行了%s函数\n' % (time_now, func.__name__))
        ret = func(*args, **kwargs)
        '''函数执行之后操作'''
        return ret
    return inner

@wrapper
def func1():
    time.sleep(1)
    print(555)
@wrapper
def func2():
    time.sleep(2)
    print(666)
func1()
func2()

 

posted @ 2018-05-19 23:45  老虎死了还有狼  阅读(679)  评论(0编辑  收藏  举报