函数高级:装饰器

print('装饰器知识总结与练习'.center(50,"#"))
'''
一,函数对象
要理解装饰器,首先要理解函数对象的概念,简单的说,当我们定义了一个函数,
def func():
pass
调用的时候,直接func() 后面加括号就表示调用函数并且运行
这里的func 这个变量名,绑定的就是函数函数程序块的一个地址,
这个变量名,就是我们的函数对象
函数对象后面加括号,表示函数的调用
这个函数对象还可以再赋值给其他变量,例如:
f1 = func 把func这个函数对象赋值给f1,绑定在一起,
现在执行原来的函数 也可以通过f1加括号实现,也就是
f1() = func() 这两个的效果是一样的
二 名称空间
什么是名称空间?名称空间:存放变量名字的地方
名称空间的加载顺序
#1、python解释器先启动,因而首先加载的是:内置名称空间
#2、执行test.py文件,然后以文件为基础,加载全局名称空间
#3、在执行文件的过程中如果调用函数,则临时产生局部名称空间

三 作用域
#1、作用域即范围
- 全局范围(内置名称空间与全局名称空间属于该范围):全局存活,全局有效
  - 局部范围(局部名称空间属于该范围):临时存活,局部有效
#2、作用域关系是在函数定义阶段就已经固定的,与函数的调用位置无关,如下
#3、查看作用域:globals(),locals()

LEGB 代表名字查找顺序: locals -> enclosing function -> globals -> __builtins__
locals 是函数内的名字空间,包括局部变量和形参
enclosing 外部嵌套函数的名字空间(闭包中常见)
globals 全局变量,函数定义所在模块的名字空间
builtins 内置模块的名字空间
总结:在一个有着嵌套函数的最内部函数中,查找一个变量,首先在当前函数的局部名称空间中查找,查找不到
的话就在外面一层函数的名称空间中查找,找不到就再往外找,一直往外找,最后找全局的,找内置的,实在找不到
就报错,也就是说从内向外找,不能向里面查找.


四:闭包与闭包函数
要理解装饰器,首先要理解闭包与闭包函数,装饰器其实就是闭包函数的一种应用
我们首先来看闭包
一 什么是闭包?
#内部函数中有对外部作用域外部的变量而非全局作用域的引用,也就是内层的东西用到了外层的变量参数
外层函数将内层函数包裹起来,并且给内层函数传递参数,这种结构叫做闭包
#提示:之前我们都是通过参数将外部的值传给函数,闭包提供了另外一种思路,
包起来喽,包起呦,包起来哇
def counter():
n=0
def incr():
print(n)
return incr
counter() ####这条语句不会打印任何内容,
counter()() ####这条语句会打印出n的值出来
# counter() = incr
# counter()()= incr()
二 ,闭包的意义与应用
#闭包的意义:返回的函数对象,不仅仅是一个函数对象,在该函数外还包裹了一层作用域,这使得,该函数无论在何处调用,
优先使用自己外层包裹的作用域
#应用领域:延迟计算(原来我们是传参,现在我们是包起来)

好了,铺垫了那么多的知识点,我们现在正式开始我们的装饰器的学习
五,装饰器:
装饰器就是闭包函数的一种应用场景
1,为何要用装饰器
为了实现与满足开放封闭原则:对修改封闭,对扩展开放

2, 什么是装饰器
装饰器他人的器具,本身可以是任意可调用对象,被装饰者也可以是任意可调用对象。
强调装饰器的原则:
1 不修改被装饰对象的源代码
2 不修改被装饰对象的调用方式
装饰器的目标:在遵循1和2的前提下,为被装饰对象添加上新功能

3,实例:装饰器的使用


'''

#####应用场景:现在又这个一个foo的函数,要求为这个函数增加新的功能,计算这个函数的运行时间并输出,
###原来的函数
# import time
#
# def foo():
# time.sleep(2)
# print('from foo')
# ###如果我们直接修改这个函数的源码,可以这样做
#
# def foo1():
# start_time = time.time()
# ##获取函数开始执行的时间
# time.sleep(2)
# print('from foo')
# ###原来函数的代码
# end_time = time.time()
# ###获取函数结束的时间
# print(end_time - start_time)

# foo1()

# foo()
###这种做法实现了这种功能,达到了我们的需求,但是我们修改了函数调用的方式,
####之前我们都是用foo调用这个函数,现在我们是用foo1调用这个函数,
###修改了函数的调用方式,如果这个函数在其他地方经常被调用的话,那么我们就需要在
# 所有的程序中全部修改这个调用方式,这样不太好,
##如果我们采用赋值foo = foo1 这样可以不改变调用方式,但是原来的foo绑定的函数块也就
##没有任何东西绑定了,也就会被回收,只剩下了foo1,那么这也就相当于改变了源代码,
# 这种做法也不可取
##接下来我们看看如何用装饰器完美的解决这个问题
##运用装饰器的做法:

# import time
# def timmer(func):
# def wrapper(*args,**kwargs):
# start_time=time.time()
# ####在这个位置原来函数执行之前可以添加装饰代码
# res=func(*args,**kwargs)
# ####在这个位置原来函数执行之后也可以添加装饰代码
# stop_time=time.time()
# print(stop_time-start_time)
# return res
# return wrapper
# @timmer ###这个语法叫做语法糖,在这里等价于 foo = timmer(foo)
# def foo():
# time.sleep(2)
# print('from foo')
# foo()
'''
上面的实例是没有参数的装饰器,下面来演示一个有参数的装饰器
需求:
原来的函数:这个函数就只有一个小功能,就是接收到输入的名字,并且打印
他是世界上最牛的程序员

def foo(name):
print('%s是这个世界上牛的程序员' %name)

现在要给这个函数添加一个登录认证的功能,在使用这个功能之前,要先登录


def auth(func):
def inner(*args,**kwargs):
while True:
name = input('请输入您的账号===>').strip()
pwd = input('请输入您的密码===>').strip()
if name == 'szp' and pwd == '123':
print('登录成功')
res = func(*args,**kwargs)
return res
else:
print('账号或者密码不正确,请重新输入')
return inner


@auth
def foo(name):
print('%s是这个世界上牛的程序员' % name)

foo('萧十一郎')

更加复杂的装饰器应用:

def auth(driver='file'):
def auth2(func):
def wrapper(*args,**kwargs):
name=input("user: ")
pwd=input("pwd: ")

if driver == 'file':
if name == 'egon' and pwd == '123':
print('login successful')
res=func(*args,**kwargs)
return res
elif driver == 'ldap':
print('ldap')
return wrapper
return auth2

@auth(driver='file')
def foo(name):
print(name)

foo('egon')
'''
'''

'''
#一:编写函数,(函数执行的时间是随机的)
# import time
# import random
# def func():
# delay_time_num = random.randint(1, 3)
# time.sleep(delay_time_num)
# print('hello world')
# print('函数延迟了%s秒才执行' % delay_time_num)
#
# func()

##z在上一题的基础上为函数编写装饰器,统计函数的执行时间
# import time
# import random
#
# def time_counter(func):
# def inner(*args,**kwargs):
# start_time = time.time()
# res = func(*args,**kwargs)
# end_time = time.time()
# print('函数的执行时间是%s秒'%(end_time - start_time))
# return res
# return inner
# @time_counter
# def func():
# delay_time_num = random.randint(1, 3)
# time.sleep(delay_time_num)
# print('hello world')
# print('函数延迟了%s秒才执行' % delay_time_num)
#
# func()


#三:编写装饰器,为函数加上登录认证的功能
from interface import user_interface

user_login = {'name':None,'pwd':'','money':0}
##登录
def denglu():
print('登录功能'.center(50,"*"))
with open(r'D:\ATM\usr_info', 'r', encoding='utf-8') as f:
date = f.read()
# print(date)
user_name, user_pwd, user_money = date.split('|')
while True:
name = input('请输入您的账号').strip()
if name != user_name:
continue
pwd = input('请输入您的密码').strip()
if pwd != '123':
continue
user_login['name'] = name
user_login['pwd'] = pwd
user_login['money'] = 100#######默认账户余额设置为100
print('登录成功')
break



# user_login2 = {'name': 'szp', 'pwd': '123', 'money': 100}

###查看账户余额
def check_balance(name):
print(user_login['money'])


denglu()
check_balance()
posted @ 2019-05-16 13:19  同济小孙  阅读(265)  评论(0编辑  收藏  举报