python闭包、装饰器

对python新手而言,要理解闭包、装饰器,就要先懂下面这些东东!

先来看下一个函数接受另外一个函数以及一个数字当作参数,并且重复调用指定函数指定次数

cat test.py           
def hello():              # hello函数
    print("hello")
def repeat(fn,times):      # 函数名是函数的入口地址
    for i in range(times):
       fn()
repeat(hello,3)

python test.py     # 执行结果
hello
hello
hello

函数也可以在其它函数内部声明

cat test.py       
def print_integer(values):                
    def is_integer(value):
        try:
           return value == int(value)
        except:
           return False
    for v in values:
        if is_integer(v):
           print(v)
           
python test.py       #  执行结果
1
2
3

# 相比函数当作参数传递,我们可以将它包装在另外的函数中,从而向函数增加新的行为。

# 向函数增加跟踪输出,有助于我们理解
def hello():
    print("hello")
def repeat(fn,times):
    for i in range(times):
       fn()
def print_call(fn):
    def fn_wrap(*args, **kwargs): #take any arguments
        print ("Calling %s" % (fn.func_name))
        return fn(*args, **kwargs) #pass any arguments to fn()
    return fn_wrap
test = print_call(hello)
print "Returning func name: %s" % test.func_name
repeat(test,2)
python test.py   # 执行结果
Returning func name: fn_wrap   # 函数改变了
Calling hello
hello
Calling hello
hello

# 如果想包装一个函数同时保留它原来的函数名,可以这样实现
def print_call(fn):
    def fn_wrap(*args, **kwargs): #take any arguments
        print ("Calling %s" % (fn.func_name))
        return fn(*args, **kwargs) #pass any arguments to fn()
    fn_wrap.func_name = fn.func_name  # 原来的函数名赋值给返回的函数名
    return fn_wrap
python test.py     # 执行结果
Returning func name: hello  # 函数名保持一致
Calling hello
hello
Calling hello
hello

# 下面该讲闭包了,闭包的定义有很多,简单点说就是:一个可以引用在函数闭合范围内变量的函数
a = 3
def get_a():
   return a
print get_a()
python test.py  # 执行结果
3

a = 3
def get_a():
   return a
def set_a(val):
   a = val
set_a(5)
print get_a()
python test.py  # 执行结果还是3
3
# 这里闭包不能写入任何被捕获的变量,a = val实际上写入了本地变量a,隐藏了全局的a。为了解决这种限制,可以用一个容器类型
class A(object):   
  pass
a = A()
a.value = 3
def get_a():
   return a.value
def set_a(val):
   global a
   a.value = val

set_a(5)
print get_a()

python test.py   # 执行结果
5

# 我们知道函数从它的闭合范围捕获变量,现在我们可以学习偏函数(prtial)了
# 一个偏函数是一个填充了部分或全部参数的函数的实例,很多时候偏函数可以消除代码的重复
# 偏函数默认pythhn库已经封装好了,只需要from functools import partial即可

def get_stuff(user,pw,stuff_id):
    print("user: %s,pw: %s,stuff_id: %s" % (user,pw,stuff_id))

def partial(fn,*args,**kwargs):
    def fn_part(*fn_args,**fn_kwargs):       # *可以传参tuple, **可以传参dict
        kwargs.update(fn_kwargs)
        return fn(*args + fn_args, **kwargs)
    return fn_part

my_stuff = partial(get_stuff,'user','pass')
my_stuff(5)

python test2.py     #  执行结果 
user: user,pw: pass,stuff_id: 5  

# 最后我们来看看函数装饰器,函数装饰器接受一个函数作为参数然后返回一个新的函数。
# 装饰器本质是:改变了函数的代码入口点
# 装饰器就是一个函数,这个函数可以是内置的(@staticmethod、@classmethod),也可以是自定义的函数。
def print_call(fn):
    def fn_wrap(*args, **kwargs): #take any arguments
        print ("Calling %s" % (fn.func_name))
        return fn(*args, **kwargs) #pass any arguments to fn()
    fn_wrap.func_name = fn.func_name
    return fn_wrap
@print_call                # 使用@符号标记,挺方便的!
def will_be_logged(arg):
    return arg*5
print will_be_logged('#')

python test.py     #  执行结果 
Calling will_be_logged
#####

# 内置修饰符
# @staticmethod vs @classmethod
# @staticmethod不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样,只能直接类名.属性名或类名.方法名。
# @classmethod也不需要self参数,但第一个参数需要是表示自身类的cls参数,有cls参数,可以来调用类的属性,类的方法,实例化对象。
# @property 对类属性的操作,类似于java中定义getter/setter
class A(object):  
    bar = 1  
    def foo(self):  
        print 'foo'  
 
    @staticmethod  
    def static_foo():  
        print 'static_foo'  
        print A.bar  
 
    @classmethod  
    def class_foo(cls):  
        print 'class_foo'  
        print cls.bar  
        cls().foo()  
  
A.static_foo()  
A.class_foo()

输出结果:
static_foo
1
class_foo
1
foo
  
# @property 对类属性的操作,类似于java中定义getter/setter
class B():
    def __init__(self):
        self.__prop = 1
    @property
    def prop(self):
        print "call get"
        return self.__prop
    @prop.setter
    def prop(self, value):
        print "call set"
        self.__prop = value
    @prop.deleter
    def prop(self):
        print "call del"
        del self.__prop
  
  
# @引用多个函数装饰器
def makebold(fn):
    def wrapped():
        return "<b>" + fn() + "</b>"
    return wrappeddef 
def makeitalic(fn):
    def wrapped():
        return "<i>" + fn() + "</i>"
    return wrapped
@makebold
@makeitalic
def hello():
    return "hello world"

print hello()  # 执行结果如下
<b><i>hello world</i></b>


# 给装饰器传参
def require(role):
     def wrapper(fn):
         def new_fn(*args, **kwargs):
             if not role in kwargs.get('roles', []):
                 print("%s not in %s" % (role, kwargs.get('roles', [])))
                 raise Exception("Unauthorized")
             return fn(*args, **kwargs)
         return new_fn
     return wrapper
@require('admin')
def get_users(**kwargs):
     return ('Alice', 'Bob')
print get_users(roles=['user','admin'])

python test.py     # 执行结果
('Alice', 'Bob')
# 函数装饰器就是这样了!未来可能有类装饰器。

扩展


参考链接

http://book42qu.readthedocs.org/en/latest/python/python-closures-and-decorators.html

http://blog.csdn.net/terry_tusiki/article/details/14223649

http://blog.csdn.net/handsomekang/article/details/9615239

http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001386820062641f3bcc60a4b164f8d91df476445697b9e000(@property sample)



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章