欢迎来到夜的世界

莫听穿林打叶声,何妨吟啸且徐行。竹杖芒鞋轻胜马,谁怕?一蓑烟雨任平生.料峭春风吹酒醒,微冷,山头斜照却相迎。回首向来萧瑟处,归去,也无风雨也无晴。
扩大
缩小

装饰器

            前戏             

 1 . 作用域 : 

 

 2 . 函数即对象 : 

  在python的世界里,函数与列表,字符串等([1,2,3],"123as",888)一样都是对象,而且函数是最高级的对象(对象是类的实例化,可以调用相应的方法,函数是包含变量对象的对象).

def foo():
    print('i am the foo')
    bar()
     
def bar():     
    print('i am the bar')
 
foo()
# def bar():      #报错
#     print('i am the bar')

  函数在内存的存储情况 : 

函数对象的调用仅仅比其它对象多了一个()而已!foo,bar与a,b一样都是个变量名。

那上面的问题也就解决了,只有函数加载到内存才可以被调用。

既然函数是对象,那么自然满足下面两个条件:

 1 . 其可以被赋给其他变量 : 

def foo():
    print('foo')
bar=foo
bar()
foo()
print(id(foo),id(bar))  #4321123592 4321123592

2 . 其可以被定义在另一个函数内(作为参数&作为返回值),类似于整形,字符串等对象 : 

#*******函数名作为参数**********
def foo(func):
    print('foo')
    func()
 
def bar():
    print('bar')
 
foo(bar)
 
#*******函数名作为返回值*********
 
def foo():
    print('foo')
    return bar
 
def bar():
    print('bar')
 
b=foo()
b()

注意:这里说的函数都是指函数名,比如foo;而foo()已经执行函数了,foo()是什么类型取决于return的内容是什么类型!!!

         另外,如果大家理解不了对象,那么就将函数理解成变量,因为函数对象总会由一个或多个变量引用,比如foo,bar。

 3 . 函数的嵌套和闭包 : 

      例如 : 

def foo():
    print('foo')
    def bar():
        print('bar')
    # bar()
bar()

是的,bar就是一个变量名,有自己的作用域的。

Python允许创建嵌套函数。通过在函数内部def的关键字再声明一个函数即为嵌套:

#想执行inner函数,两种方法
def outer():
     x = 1
     def inner():
         print (x) # 1
     # inner() # 2
     return inner
 
# outer()
in_func=outer()
in_func()

   1 . 两种调用方式的区别 : 

in_func=outer() 
in_func()  
###########
inner()(已经加载到内存啦)
def outer():
     x = 1
     def inner():
         b=6
         print (x)
     return inner
#inner()#报错原因:找不到这个引用变量
in_func=outer()#这里其实就是一个变量赋值,将inner的引用对象赋值给in_func,类似于a=5,b=a一样
               #有同学会想直接赋值不行吗:in_func=inner? 哥,inner不还是找不到吗,对吧
in_func()
原因

   2 . 

def outer():
    x=1    #函数outer执行完毕即被销毁
print(x)    

既然这样,i()执行的时候outer函数已经执行完了,为什么inner还可以调用outer里的变量x呢?

因为:outer里return的inner是一个闭包函数,有x这个环境变量。

 OK,那么什么是闭包呢?

闭包(closure)是函数式编程的重要的语法结构。

定义:如果在一个内部函数里,对在外部作用域(但不是在全局作用域)的变量进行引用,那么内部函数就被认为是闭包(closure).

如上实例,inner就是内部函数,inner里引用了外部作用域的变量x(x在外部作用域outer里面,不是全局作用域),

则这个内部函数inner就是一个闭包。

再稍微讲究一点的解释是,闭包=函数块+定义函数时的环境,inner就是函数块,x就是环境,当然这个环境可以有很多,不止一个简单的x。

print(in_func.__closure__[0].cell_contents) 

 

# 用途1:当闭包执行完后,仍然能够保持住当前的运行环境。
# 比如说,如果你希望函数的每次执行结果,都是基于这个函数上次的运行结果。我以一个类似棋盘游戏的例子
# 来说明。假设棋盘大小为50*50,左上角为坐标系原点(0,0),我需要一个函数,接收2个参数,分别为方向
# (direction),步长(step),该函数控制棋子的运动。棋子运动的新的坐标除了依赖于方向和步长以外,
# 当然还要根据原来所处的坐标点,用闭包就可以保持住这个棋子原来所处的坐标。

origin = [0, 0] # 坐标系统原点
legal_x = [0, 50] # x轴方向的合法坐标
legal_y = [0, 50] # y轴方向的合法坐标
def create(pos=origin):
 def player(direction,step):
  # 这里应该首先判断参数direction,step的合法性,比如direction不能斜着走,step不能为负等
  # 然后还要对新生成的x,y坐标的合法性进行判断处理,这里主要是想介绍闭包,就不详细写了。
  new_x = pos[0] + direction[0]*step
  new_y = pos[1] + direction[1]*step
  pos[0] = new_x
  pos[1] = new_y
  #注意!此处不能写成 pos = [new_x, new_y],原因在上文有说过
  return pos
 return player

player = create() # 创建棋子player,起点为原点
print (player([1,0],10)) # 向x轴正方向移动10步
print (player([0,1],20)) # 向y轴正方向移动20步
print (player([-1,0],10)) # 向x轴负方向移动10步
用途1
# 用途2:闭包可以根据外部作用域的局部变量来得到不同的结果,这有点像一种类似配置功能的作用,我们可以
# 修改外部的变量,闭包根据这个变量展现出不同的功能。比如有时我们需要对某些文件的特殊行进行分析,先
# 要提取出这些特殊行。

def make_filter(keep):
 def the_filter(file_name):
  file = open(file_name)
  lines = file.readlines()
  file.close()
  filter_doc = [i for i in lines if keep in i]
  return filter_doc
 return the_filter

# 如果我们需要取得文件"result.txt"中含有"pass"关键字的行,则可以这样使用例子程序
filter = make_filter("pass")
filter_result = filter("result.txt")
用途2

             装饰器               

1 . 为什么要使用装饰器呢 ? 

  装饰器的功能:在不修改原函数及其调用方式的情况下对原函数功能进行扩展

  装饰器的本质:就是一个闭包函数

那么我们先来看一个简单的装饰器:实现计算每个函数的执行时间的功能

 1 import time 
 2 def  wrapper(func):
 3         def inner():
 4               start=time.time()
 5               func()
 6               end=time.time()
 7               print(end-start)
 8         return inner 
 9     
10 def  hahaha():
11         time.sleep(1)
12         print('aaaaa')
13 hahaha=wrapper(hahaha)
14 hahaha()    
简单的装饰器

上面的代码有点不简洁,不完美,下面引入了语法糖

1 import time
 2 def wrapper(func):
 3         def inner():
 4                start=time.time()
 5                func()
 6                end=time.time()
 7                print(end-start)
 8         return inner
 9 @wrapper
10 def  kkk():#相当于kkk=wrapper(kkk)
11     print('aaaaa')
12 kkk()      
语法糖

 

 2 . 带参数的装饰器 : 

带参数的装饰器:就是给装饰器传参

        用处:就是当加了很多装饰器的时候,现在忽然又不想加装饰器了,想把装饰器给去掉了,但是那么多的代码,一个一个的去闲的麻烦,那么,我们可以利用带参数的装饰器去装饰它,这就他就像一个开关一样,要的时候就调用了,不用的时候就去掉了。给装饰器里面传个参数,那么那个语法糖也要带个括号。在语法糖的括号内传参。在这里,我们可以用三层嵌套,弄一个标识为去标识。如下面的代码示例

 1 # 带参数的装饰器:(相当于开关)为了给装饰器传参
 2 # F=True#为True时就把装饰器给加上了
 3 F=False#为False时就把装饰器给去掉了
 4 def outer(flag):
 5     def wrapper(func):
 6         def inner(*args,**kwargs):
 7             if flag:
 8                 print('before')
 9                 ret=func(*args,**kwargs)
10                 print('after')
11             else:
12                 ret = func(*args, **kwargs)
13             return ret
14         return inner
15     return wrapper
16 
17 @outer(F)#@wrapper
18 def hahaha():
19     print('hahaha')
20 
21 @outer(F)
22 def shuangwaiwai():
23     print('shuangwaiwai')
24 
25 hahaha()
26 shuangwaiwai()

  3 . 多个装饰器带一个参数 : 

 1 def qqqxing(fun):
 2     def inner(*args,**kwargs):
 3         print('in qqxing: before')
 4         ret = fun(*args,**kwargs)
 5         print('in qqxing: after')
 6         return ret
 7     return inner
 8 
 9 def pipixia(fun):
10     def inner(*args,**kwargs):
11         print('in qqxing: before')
12         ret = fun(*args,**kwargs)
13         print('in qqxing: after')
14         return ret
15     return inner
16 @qqqxing
17 @pipixia
18 def dapangxie():
19     print('饿了吗')
20 dapangxie()
21 
22 '''
23 @qqqxing和@pipixia的执行顺序:先执行qqqxing里面的 print('in qqxing: before'),然后跳到了pipixia里面的
24         print('in qqxing: before')
25         ret = fun(*args,**kwargs)
26         print('in qqxing: after'),完了又回到了qqqxing里面的 print('in qqxing: after')。所以就如下面的运行结果截图一样
''''
View Code

  4 . 练习 : 

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

定义一个字典看用户登录状态
auth_status={
    'user':None,
    'status':False#状态是没有登录
}
def wrapper(fun):
    def inner(*args,**kwargs):
        if auth_status['status']:#如果登录了,就执行fun函数
            ret = fun(*args, **kwargs)  # index/home
            return ret
        else:#如果没有登录,就实现认证功能
            username = input('name:>>').strip()#获取用户名
            password = input('password:>>').strip()#获取密码
            f=open('login.txt','r',encoding='utf-8')#打开文件获取用户的信息
            user_dic=f.read()#读出来的是字符串类型
            zhddict=eval(user_dic)#转换成字典类型
            #print(type(zhddict))#查看eval转换后的类型
            if zhddict.get(username) and password == zhddict[username]:#如果用户名和密码都正确,就显示登陆成功,不正确就显示登录失败
                print('login successful')
                auth_status['user']=username #登陆成功后就把用户名放在user里
                auth_status['status']=True    状态改为True
                ret = fun(*args,**kwargs)#index/home
                return ret
            else:
                print('login faild')
    return inner
@wrapper #语法糖
def index():
    print("欢迎来到首页")

@wrapper
def home():
    print("欢迎回家")

index()
index()
home()
index()
View Code

   (2) . 

def login(func):

    def inner():
        user = input("user:>>>>")
        pwd = input("pwd:>>>>")

        if user == "alex" and pwd == "123":
            print("success!")
            func()
        else:
            print("error!")
    return inner

@login   #  index=login(index)
def index():
     print("this is index!")

# index()

@login
def bar():
    print("bar.......")

bar()
View Code

 

posted on 2018-11-08 18:02  二十四桥_明月夜  阅读(183)  评论(0编辑  收藏  举报

导航