Python基础知识07-装饰器

一:命名空间与作用域

1.1命名空间

Python使用叫做命名空间的东西来记录变量的轨迹。命名空间是一个 字典(dictionary) ,它的键就是变量名,它的值就是那些变量的值。

在一个 Python 程序中的任何一个地方,都存在几个可用的命名空间。
     1、每个函数都有着自已的命名空间,叫做局部命名空间,它记录了函数的变量,包括函数的参数和局部定义的变量。
     2、每个模块拥有它自已的命名空间,叫做全局命名空间,它记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。
     3、还有就是内置命名空间,任何模块均可访问它,它存放着内置的函数和异常。

局部命名空间:

def foo():
    x=1
    def func():
        pass

全局命名空间:

import time
class ClassName:
    pass
def foo():
    pass

内键命名空间:

sum,max,min 等

1.2命名空间查找顺序

当一行代码要使用变量 x 的值时,Python 会到所有可用的名字空间去查找变量,按照如下顺序:
     1、局部命名空间:特指当前函数或类的方法。如果函数定义了一个局部变量 x,或一个参数 x,Python 将使用它,然后停止搜索。
     2、全局命名空间:特指当前的模块。如果模块定义了一个名为 x 的变量,函数或类,Python 将使用它然后停止搜索。
     3、内置命名空间:对每个模块都是全局的。作为最后的尝试,Python 将假设 x 是内置函数或变量。
     4、如果 Python 在这些命名空间找不到 x,它将放弃查找并引发一个 NameError 异常,如,NameError: name 'aa' is not defined。
 
嵌套函数的情况:
     1、先在当前 (嵌套的或 lambda) 函数的命名空间中搜索
     2、然后是在父函数的命名空间中搜索
     3、接着是模块命名空间中搜索
     4、最后在内置命名空间中搜索
#!/usr/bin/env python
#_*_ coding:utf-8 _*_

info = "Adress : "
def func_father(country):
    city = " Beijing "
    def func_son(area):
        city= "Shanghai " #此处的city变量,覆盖了父函数的city变量
        print(info + country + city + area)    
    #调用内部函数
    func_son("ChaoYang ");
 
func_father("China ")
#输出结果
#Adress : China Shanghai ChaoYang

#info在全局命名空间中,country在父函数的命名空间中,city、area在自己函数的命名空间中
示例

1.3命名空间的生命周期

不同的命名空间在不同的时刻创建,有不同的生存期。
     1、内置命名空间在 Python 解释器启动时创建,会一直保留,不被删除。
     2、模块的全局命名空间在模块定义被读入时创建,通常模块命名空间也会一直保存到解释器退出。
     3、当函数被调用时创建一个局部命名空间,当函数返回结果 或 抛出异常时,被删除。每一个递归调用的函数都拥有自己的命名空间。
 
  Python 的一个特别之处在于其赋值操作总是在最里层的作用域。赋值不会复制数据——只是将命名绑定到对象。删除也是如此:"del y" 只是从局部作用域的命名空间中删除命名 y 。事实上,所有引入新命名的操作都作用于局部作用域。
i=1
def func2():
    i=i+1
 
func2();
#错误:UnboundLocalError: local variable 'i' referenced before assignment
示例

 由于创建命名空间时,python会检查代码并填充局部命名空间。在python运行那行代码之前,就发现了对i的赋值,并把它添加到局部命名空间中。当函数执行时,python解释器认为i在局部命名空间中但没有值,所以会产生错误。

def func3():
  y=123
  del y
  print(y)

func3()
#错误:UnboundLocalError: local variable 'y' referenced before assignment
#去掉"del y"语句后,运行正常
示例 

1.3命名空间的访问

1.3.1局部命名空间可以 locals()  BIF来访问。

locals 返回一个名字/值对的 dictionary。这个 dictionary 的键是字符串形式的变量名字,dictionary 的值是变量的实际值。
示例:
def func(i, arg):
    x = 12345
    print locals()
func(1 , "first")
#输出结果
#{'i': 1, 'x': 12345, 'arg': 'first'}

1.3.2全局 (模块级别)命名空间可以通过 globals() BIF来访问。

#!/usr/bin/env python
#_*_ coding:utf-8 _*_

import copy
from copy import deepcopy
 
gstr = "global string"
 
def func1(i, info):
    x = 12345
    print locals()

if __name__ == "__main__":
    func1(1 , "first")
    print("the current scope's global variables:")
    print globals()
#输出结果
{'info': 'first', 'x': 12345, 'i': 1}
the current scope's global variables:
{'func1': <function func1 at 0x02661470>, 'gstr': 'global string', '__builtins__': <module '__builtin__' (built-in)>, '__file__': 'D:\\Python\\test\\src\\test1.py', '__package__': None, 'deepcopy': <function deepcopy at 0x026611F0>, '__name__': '__main__', 'copy': <module 'copy' from 'C:\Python27\lib\copy.pyc'>, '__doc__': None}
示例
总结
  1、模块的名字空间不仅仅包含模块级的变量和常量,还包括所有在模块中定义的函数和类。除此以外,它还包括了任何被导入到模块中的东西。
  2、我们看到,内置命名也同样被包含在一个模块中,它被称作 __builtin__。
  3、回想一下 from module import 和 import module 之间的不同。
    使用 import module,模块自身被导入,但是它保持着自已的名字空间,这就是为什么您需要使用模块名来访问它的函数或属性:module.function 的原因。
    但是使用 from module import function,实际上是从另一个模块中将指定的函数和属性导入到您自己的名字空间,这就是为什么您可以直接访问它们却不需要引用它们所来源的模块。使用 globals 函数,您会真切地看到这一切的发生,见上面的红色输出语句。

1.3.3locals 与 globals 之间的一个重要的区别

locals 是只读的,globals 不是

#!/usr/bin/env python
#_*_ coding:utf-8 _*_

def func1(i, info):
    x = 12345
    print(locals())
    locals()["x"]= 6789
    print("x=",x)
y=54321
func1(1 , "first")
globals()["y"]= 9876
print( "y=",y)
#输出结果
{'info': 'first', 'x': 12345, 'i': 1}
('x=', 12345)
('y=', 9876)
解释:
  locals 实际上没有返回局部名字空间,它返回的是一个拷贝。所以对它进行改变对局部名字空间中的变量值并无影响。
  globals 返回实际的全局名字空间,而不是一个拷贝。所以对 globals 所返回的 dictionary 的任何的改动都会直接影响到全局变量。

1.4作用域

作用域搜索规则:LEGB

L:局部的(local)

E:封闭的(Enclosing)

G:全局的(Global)

B:内置的(Built-in)

全局作用域与局部作用域:

#全局变量作用域,x=1为全局变量
x=1
def func1():
    def func2():
        def func3():
            print(x)
        func3()
    func2()
func1()
#输出结果
#1

#局部变量作用域
x=1
def func1():
    def func2():
        def func3():
            x=100
            print(x)
        func3()
    func2()
func1()
#输出结果:
#100
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
-------------------------------------------------------------------------------
Function:
【整理】Python中:self和init__的含义 + 为何要有self和__init__
https://www.crifan.com/summary_the_meaning_of_self_and___init___in_python_and_why_need_them
 
Author:     Crifan
Verison:    2012-11-27
-------------------------------------------------------------------------------
"""
 
#注:此处全局的变量名,写成name,只是为了演示而用
#实际上,好的编程风格,应该写成gName之类的名字,以表示该变量是Global的变量
name = "whole global name";
 
class Person:
    name = "class global name"
 
    def __init__(self, newPersionName):
        #self.name = newPersionName;
         
        #此处,没有使用self.name
        #而使得此处的name,实际上仍是局部变量name
        #虽然此处赋值了,但是后面没有被利用到,属于被浪费了的局部变量name
        name = newPersionName;
 
    def sayYourName(self):
        #此处,之所以没有像之前一样出现:
        #AttributeError: Person instance has no attribute 'name'
        #那是因为,虽然当前的实例self中,没有在__init__中初始化对应的name变量,实例self中没有对应的name变量
        #但是由于实例所对应的类Person,有对应的name变量,所以也是可以正常执行代码的
        #对应的,此处的self.name,实际上是Person.name
        print 'My name is %s'%(self.name); # -> class global name
        print 'name within class Person is actually the global name: %s'%(name); #-> whole global name
        print "only access Person's name via Person.name=%s"%(Person.name); # -> class global name
 
def selfAndInitDemo():
    persionInstance = Person("crifan");
    persionInstance.sayYourName();
    print "whole global name is %s"%(name); # -> whole global name
     
###############################################################################
if __name__=="__main__":
    selfAndInitDemo();
示例

二:装饰器

2.1 什么是装饰器

器即函数

装饰即修饰,意指为其他函数添加新功能

装饰器定义:本质就是函数,功能是为其他函数添加新功能

2.2 实现装饰器知识储备

装饰器=高阶函数+函数嵌套+闭包

2.3 装饰器需要遵循的原则

1.不修改被装饰函数的源代码(开放封闭原则)

2.为被装饰函数添加新功能后,不修改被修饰函数的调用方式

3装饰器是在遵守1和2的前提下,为被装饰的对象添加新功能

2.4 装饰器无参基本框架

#这就是一个实现一个装饰器最基本的架子
def timer(func):
    def wrapper():
        func()
    return wrapper

2.5 装饰器有参基本框架

def timer(func):
    def wrapper(*args,**kwargs):
        func(*args,**kwargs)
    return wrapper

2.6 无参装饰器

#!/usr/bin/env python
#_*_ coding:utf-8 _*_

import time
def timer(func): #func=最原始的index
    def warpper():
        start_time=time.time()
        func()
        end_time=time.time()
        print('run time is %s' % (end_time - start_time))
    return warpper

#无参装饰器
@timer #index=timer(index) 此时的index就是warpper
def index():
    time.sleep(1)
    print("welcome to index")
index()#就是在调用warpper()
#原来的index的函数只会输出welcome to index
#加上装饰器后;
#输出内容为:
#welcome to index
#run time is 1.0

2.7 有参数装饰器

#!/usr/bin/env python
#_*_ coding:utf-8 _*_

import time
def timer(func): #func=最原始的index
    def warpper(user):
        start_time=time.time()
        func(user)
        end_time=time.time()
        print('run time is %s' % (end_time - start_time))
    return warpper

@timer #index=timer(index) 此时的index就是warpper
def home(user):
    time.sleep(1)
    print("home %s"% user)
    
home('tom')#就是在调用warpper()
#输出结果:
#home tom
#run time is 1.0

2.8 如果传入的参数是关键字类型

#!/usr/bin/env python
#_*_ coding:utf-8 _*_

import time
def timer(func): #func=最原始的index
    def warpper(*args,**kwargs):
        start_time=time.time()
        func(*args,**kwargs)
        end_time=time.time()
        print('run time is %s' % (end_time - start_time))
    return warpper
 
@timer #index=timer(index) 此时的index就是warpper
def home(user):
    time.sleep(1)
    print("home %s"% user)

home('tom')#就是在调用warpper()
home(user="yyp")
#输出结果:
#home tom
#run time is 1.0
#home yyp
#run time is 1.0

2.9 如果原函数有返回值

#!/usr/bin/env python
#_*_ coding:utf-8 _*_

import time
def timer(func): #func=最原始的index
    def warpper(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        end_time=time.time()
        print('run time is %s' % (end_time - start_time))
        return res
    return warpper

@timer #index=timer(index) 此时的index就是warpper
def home(user):
    time.sleep(1)
    return user + ' is student'

res=home('tom')#就是在调用warpper()
print(res)
#输出结果:
#run time is 1.0
#tom is student

三:闭包

闭包:本质就是一个内部函数

特点:这个内部函数必须包含对外部函数作用域(非全局作用)名字的引用

#!/usr/bin/env python
#_*_ coding:utf-8 _*_

def f1():
    x=1;y=2
    def f2():       #f2为闭包函数
        print(x)
        print(y)
    return f2

func=f1()
#func()
print(func.__closure__[0].cell_contents)#查看闭包函数的方法
print(func.__closure__[1].cell_contents)

#输出结果:
#1
#2

惰性计算:啥时候用啥时候调用

#!/usr/bin/env python
#_*_ coding:utf-8 _*_

from urllib.request import urlopen

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

baidu=page('http://www.baidu.com')
python=page('http://www.python.org')

print(baidu)
print(python)
#输出结果(函数没有执行,已经传了一个参数):
#<function page.<locals>.get at 0x0000000002DE6BF8>
#<function page.<locals>.get at 0x0000000002DE6C80>

四:装饰器应用示例

user_list=[
    {'name':'tom','passwd':'123'},
    {'name':'yyp','passwd':'123'},
    {'name':'sy','passwd':'123'},
    {'name':'ly','passwd':'123'},
]

current_user={'username':None,'login':False}

def auth_deco(func):
    def wrapper(*args,**kwargs):
        if current_user['username'] and current_user['login']:
            res=func(*args,**kwargs)
            return res
        username=raw_input('用户名: ').strip()
        passwd=raw_input('密码: ').strip()

        for index,user_dic in enumerate(user_list):     
            #enumerate:python内置函数,对于一个可迭代的(iterable)/可遍历的对象(如列表、字符串),enumerate将其组成一个索引序列,利用它可以同时获得索引和值
            print index, user_dic
            if username == user_dic['name'] and passwd == user_dic['passwd']:
                current_user['username']=username

                current_user['login']=True
                res=func(*args,**kwargs)
                return res
                break
        else:
            print('用户名或者密码错误,重新登录')

    return wrapper

@auth_deco
def index():
    print('欢迎来到主页面')

@auth_deco
def home():
    print('这里是你家')

def shopping_car():
    print('查看购物车啊亲')

def order():
    print('查看订单啊亲')

print(user_list)
index()
print(user_list)
home()
无参装饰器
user_list=[
    {'name':'tom','passwd':'123'},
    {'name':'yyp','passwd':'123'},
    {'name':'sy','passwd':'123'},
    {'name':'ly','passwd':'123'},
]

current_user={'username':None,'login':False}
def auth(auth_type='file'):
    def auth_deco(func):
        def wrapper(*args,**kwargs):
            if auth_type == 'file':
                if current_user['username'] and current_user['login']:
                    res=func(*args,**kwargs)
                    return res
                username=input('用户名: ').strip()
                passwd=input('密码: ').strip()

                for index,user_dic in enumerate(user_list):
                    if username == user_dic['name'] and passwd == user_dic['passwd']:
                        current_user['username']=username
                        current_user['login']=True
                        res=func(*args,**kwargs)
                        return res
                        break
                else:
                    print('用户名或者密码错误,重新登录')
            elif auth_type == 'ldap':
                print('巴拉巴拉小魔仙')
                res=func(*args,**kwargs)
                return res
        return wrapper
    return auth_deco


#auth(auth_type='file')就是在运行一个函数,然后返回auth_deco,所以@auth(auth_type='file')
#就相当于@auth_deco,只不过现在,我们的auth_deco作为一个闭包的应用,外层的包auth给它留了一个auth_type='file'参数
@auth(auth_type='ldap')
def index():
    print('欢迎来到主页面')

@auth(auth_type='ldap')
def home():
    print('这里是你家')

def shopping_car():
    print('查看购物车啊亲')

def order():
    print('查看订单啊亲')

# print(user_list)
index()
# print(user_list)
home()
有参装饰器

五:装饰器九步入门

# -*- coding:gbk -*-
'''示例1: 最简单的函数,表示调用了两次'''
 
def myfunc():
    print("myfunc() called.")
 
myfunc()
myfunc()
第一步:最简单的函数,准备附加额外功能
# -*- coding:gbk -*-
'''示例2: 替换函数(装饰)
装饰函数的参数是被装饰的函数对象,返回原函数对象
装饰的实质语句: myfunc = deco(myfunc)'''
 
def deco(func):
    print("before myfunc() called.")
    func()
    print("  after myfunc() called.")
    return func
 
def myfunc():
    print(" myfunc() called.")
 
myfunc = deco(myfunc)
 
myfunc()
myfunc()
第二步:使用装饰函数在函数执行前和执行后分别附加额外功能
# -*- coding:gbk -*-
'''示例3: 使用语法糖@来装饰函数,相当于“myfunc = deco(myfunc)”
但发现新函数只在第一次被调用,且原函数多调用了一次'''
 
def deco(func):
    print("before myfunc() called.")
    func()
    print("  after myfunc() called.")
    return func
 
@deco
def myfunc():
    print(" myfunc() called.")
 
myfunc()
myfunc()
使用语法糖@来装饰函数
# -*- coding:gbk -*-
'''示例4: 使用内嵌包装函数来确保每次新函数都被调用,
内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象'''
 
def deco(func):
    def _deco():
        print("before myfunc() called.")
        func()
        print("  after myfunc() called.")
        # 不需要返回func,实际上应返回原函数的返回值
    return _deco
 
@deco
def myfunc():
    print(" myfunc() called.")
    return 'ok'
 
myfunc()
myfunc()
第四步:使用内嵌包装函数来确保每次新函数都被调用
# -*- coding:gbk -*-
'''示例5: 对带参数的函数进行装饰,
内嵌包装函数的形参和返回值与原函数相同,装饰函数返回内嵌包装函数对象'''
 
def deco(func):
    def _deco(a, b):
        print("before myfunc() called.")
        ret = func(a, b)
        print("  after myfunc() called. result: %s" % ret)
        return ret
    return _deco
 
@deco
def myfunc(a, b):
    print(" myfunc(%s,%s) called." % (a, b))
    return a + b
 
myfunc(1, 2)
myfunc(3, 4)
第五步:对带参数的函数进行装饰
# -*- coding:gbk -*-
'''示例6: 对参数数量不确定的函数进行装饰,
参数用(*args, **kwargs),自动适应变参和命名参数'''
 
def deco(func):
    def _deco(*args, **kwargs):
        print("before %s called." % func.__name__)
        ret = func(*args, **kwargs)
        print("  after %s called. result: %s" % (func.__name__, ret))
        return ret
    return _deco
 
@deco
def myfunc(a, b):
    print(" myfunc(%s,%s) called." % (a, b))
    return a+b
 
@deco
def myfunc2(a, b, c):
    print(" myfunc2(%s,%s,%s) called." % (a, b, c))
    return a+b+c
 
myfunc(1, 2)
myfunc(3, 4)
myfunc2(1, 2, 3)
myfunc2(3, 4, 5)
第六步:对参数数量不确定的函数进行装饰
# -*- coding:gbk -*-
'''示例7: 在示例4的基础上,让装饰器带参数,
和上一示例相比在外层多了一层包装。
装饰函数名实际上应更有意义些'''
 
def deco(arg):
    def _deco(func):
        def __deco():
            print("before %s called [%s]." % (func.__name__, arg))
            func()
            print("  after %s called [%s]." % (func.__name__, arg))
        return __deco
    return _deco
 
@deco("mymodule")
def myfunc():
    print(" myfunc() called.")
 
@deco("module2")
def myfunc2():
    print(" myfunc2() called.")
 
myfunc()
myfunc2()
第七步:让装饰器带参数
# -*- coding:gbk -*-
'''示例8: 装饰器带类参数'''
 
class locker:
    def __init__(self):
        print("locker.__init__() should be not called.")
         
    @staticmethod
    def acquire():
        print("locker.acquire() called.(这是静态方法)")
         
    @staticmethod
    def release():
        print("  locker.release() called.(不需要对象实例)")
 
def deco(cls):
    '''cls 必须实现acquire和release静态方法'''
    def _deco(func):
        def __deco():
            print("before %s called [%s]." % (func.__name__, cls))
            cls.acquire()
            try:
                return func()
            finally:
                cls.release()
        return __deco
    return _deco
 
@deco(locker)
def myfunc():
    print(" myfunc() called.")
 
myfunc()
myfunc()
第八步:让装饰器带 类 参数
# -*- coding:gbk -*-
'''mylocker.py: 公共类 for 示例9.py'''
 
class mylocker:
    def __init__(self):
        print("mylocker.__init__() called.")
         
    @staticmethod
    def acquire():
        print("mylocker.acquire() called.")
         
    @staticmethod
    def unlock():
        print("  mylocker.unlock() called.")
 
class lockerex(mylocker):
    @staticmethod
    def acquire():
        print("lockerex.acquire() called.")
         
    @staticmethod
    def unlock():
        print("  lockerex.unlock() called.")
 
def lockhelper(cls):
    '''cls 必须实现acquire和release静态方法'''
    def _deco(func):
        def __deco(*args, **kwargs):
            print("before %s called." % func.__name__)
            cls.acquire()
            try:
                return func(*args, **kwargs)
            finally:
                cls.unlock()
        return __deco
    return _deco
第九步:装饰器带类参数,并分拆公共类到其他py文件中,同时演示了对一个函数应用多个装饰器
# -*- coding:gbk -*-
'''示例9: 装饰器带类参数,并分拆公共类到其他py文件中
同时演示了对一个函数应用多个装饰器'''
 
from mylocker import *
 
class example:
    @lockhelper(mylocker)
    def myfunc(self):
        print(" myfunc() called.")
 
    @lockhelper(mylocker)
    @lockhelper(lockerex)
    def myfunc2(self, a, b):
        print(" myfunc2() called.")
        return a + b
 
if __name__=="__main__":
    a = example()
    a.myfunc()
    print(a.myfunc())
    print(a.myfunc2(1, 2))
    print(a.myfunc2(3, 4))
装饰器带类参数,并分拆公共类到其他py文件中 同时演示了对一个函数应用多个装饰器
'''Add decorator for each method of a class.'''
import functools
import threading
 
class DecorateClass(object):
    def decorate(self):
        for name, fn in self.iter():
            if callable(fn):
                self.operate(name, fn)
 
class LockerDecorator(DecorateClass):
    def __init__(self, obj, lock=threading.RLock()):
        self.obj = obj
        self.lock = lock
 
    def iter(self):
        return [(name,getattr(self.obj, name)) \
                for name in dir(self.obj) if not name.startswith('_')]
     
    def operate(self, name, fn):
        @functools.wraps(fn)
        def locker(*args,**kv):
            self.lock.acquire()
            try:
                return fn(*args, **kv)
            finally:
                self.lock.release()
        setattr(self.obj, name, locker)
 
class mylocker:
    def __init__(self):
        print("mylocker.__init__() called.")
    def acquire(self):
        print("mylocker.acquire() called.")
    def release(self):
        print("  mylocker.unlock() called.")
 
class Foo(object):
    def __init__(self):
        # Enable one or more decorators for each method:
        LockerDecorator(self).decorate()
        LockerDecorator(self, mylocker()).decorate()
         
    def interface1(self):
        print(" interface1() called.")
 
    def interface2(self):
        print(" interface2() called.")
 
    def _interface3(self):
        print("_interface3() called.")
 
if __name__=="__main__":
    obj = Foo()
    obj.interface1()
    obj.interface2()
    obj.interface1()
    obj.interface2()
    obj._interface3()
    print(obj.interface1.__name__)
    '''
    print(dir(obj))
    print("---------------------")
    for item in [(name,getattr(obj, name)) for name in dir(obj)]:
        print(item)'''
自动为py类的每个方法添加装饰器
posted @ 2017-09-06 11:44  amonos  阅读(193)  评论(0编辑  收藏  举报