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)



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