理解python装饰器

王朝学院·作者佚名  2016-08-27
窄屏简体版  字體: 小  |  中  |  大  |  超大  

变量name = 'world'

x = 3

变量是代表某个值的名字

函数def hello(name):

return 'hello' + name

hello('Word)

hello word

函数通过def关键字、函数名和可选的参数列表定义。

是可以调用的,它执行某种行为并且返回一个值。

函数内部也可以定义函数

def outer():

x = 1

def inner():PRint x # 1

inner()

>> outer()

1

作用域&生存周期def func(x):

print 'x is', x

print locals()

func(50)

>> x is 50

{'x': 50}

>> print x

Traceback (most recent call last):

NameError: name 'x' is not defined

函数会创建一个新的作用域(命名空间)。

函数的命名空间随着函数调用开始而开始,结束而销毁

g = 'global variable'

>> print globals()

{'g': 'global variable',

'__builtins__': <module '__builtin__' (built-in)>,

'__file__': 'test.py',

'__package__': None,

'func': <function func at 0x10472d8c0>,

'__name__': '__main__',

'__doc__': None}

def foo():

g = 'test'

print 'g is', g

print locals()

print globals()

>> foo()

g is test

{'g': 'test'}

{'g': 'global variable',

'__builtins__': <module '__builtin__' (built-in)>,

'__file__': 'test.py',

'__package__': None,

'func': <function func at 0x10472d8c0>,

'__name__': '__main__',

'__doc__': None}

>> print g

global variable

在函数内部遇到变量的时候会有现在自己的命名空间里找

猜一下段代码会执行的结果是什么

g = '我已经定义了'

def foo():

print g

g = '我重新定义了'

print g

答案

函数参数函数有两种参数

位置参数

命名参数def foo(x, y=0):

return x - y

*args and **kwargs? [duplicate]

python 中一切都是对象函数和python中其他一样都是对象

In [7]: class A(object):

...: pass

In [8]: A

Out[8]: __main__.A

In [9]: type(A)

Out[9]: type

In [10]: def foo():

....: pass

In [11]: type(foo)

Out[11]: function

In [12]: A.__class__

Out[12]: type

In [13]: foo.__class__

Out[13]: function

In [14]: a = 1

In [15]: a.__class__

Out[15]: int

# 类 是对象

In [16]: issubclass(A.__class__, object)

Out[16]: True

# 变量 是对象

In [17]: issubclass(a.__class__, object)

Out[17]: True

# 函数 是对象

In [18]: issubclass(foo.__class__, object)

Out[18]: True

所以函数也可以作为参数传递给其它函数,也可以被当做返回值返回

def add(x, y):

return x + y

def apply(func):

return func

>> a = apply(add)

>> type(a)

<type 'function'>

>> a(1, 2)

>> 3

闭包def make_adder(a):

def adder(b):

return b + a

return adder

add = make_adder(5)

>> add

# output

<function adder at 0x108166140>

>> add(3)

# output

8

adder 就是一个闭包

也可以说 make_adder 指向一个闭包

或者说 make_add 是闭包的工厂函数闭包可以认为是一个内层函数(adder),由一个变量指代,而这个变量相对于外层包含它的函数而言,是本地变量

嵌套定义在非全局作用域里面的函数能够记住它在被定义的时候它所处的封闭命名空间

闭包只是在形式和表现上像函数,但实际上不是函数。函数是一些可执行的代码,这些代码在函数被定义后就确定了,不会在执行时发生变化,所以一个函数只有一个实例。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

装饰器对一个已有的模块做一些“修饰工作”,所谓修饰工作就是想给现有的模块加上一些小装饰(一些小功能,这些小功能可能好多模块都会用到),但又不让这个小装饰(小功能)侵入到原有的模块中的代码里去

def my_decorator(func):

def wrapper():

print "Before the function runs"

func()

print "After the function runs"

return wrapper

def my_func():

print "I am a stand alone function"

>> my_func()

# output

I am a stand alone function

# 然后,我们在这里装饰这个函数

# 将函数传递给装饰器,装饰器将动态地将其包装在任何想执行的代码中,然后返回一个新的函数

>> my_func = my_decorator(my_func)

>> my_func()

#output

Before the function runs

I am a stand alone function

After the function runs

# 也可以这么写

@ my_decorator

def my_func():

print "I am a stand alone function"

>> my_func()

#output

Before the function runs

I am a stand alone function

After the function runs

装饰器是设计模式中装饰器模式(英文版)的python实现

多个装饰器装饰器可以嵌套使用

def bread(func):

def wrapper():

print "</''''''\>"

func()

print "<\______/>"

return wrapper

def ingredients(func):

def wrapper():

print "#tomatoes#"

func()

print "~salad~"

return wrapper

def sandwich(food="--ham--"):

print food

#outputs:

#嵌套两个装饰器

>> sandwich = bread(ingredients(sandwich))

>> sandwich()

# outputs:

</''''''\>

#tomatoes#

--ham--

~salad~

<\______/>

更简单的写法

@bread

@ingredients

def sandwich(food="--ham--"):

print food

装饰器的顺序是很重要的

@ingredients

@bread

def sandwich(food="--ham--"):

print food

# outputs:

#tomatoes#

</' ' ' ' ' '\>

--ham--

<\______/>

~salad~

Decorator 的本质首先看一下这段代码

def deco(fn):

print "I am %s!" % fn.__name__

@deco

def func():

pass

# output

I am func!

# 没有执行func 函数 但是 deco 被执行了

在用某个 at decorator来修饰某个函数func时@decoratordef func(): pass其解释器会解释成下面这样的语句:func = decorator(func)其实就是把一个函数当参数传到另一个函数中,然后再回调但是值得注意的是装饰器必须返回一个函数给func回到刚才的例子def my_decorator(func): def wrapper(): print "Before the function runs" func() print "After the function runs" return wrapper def my_func(): print "I am a stand alone function" >> my_func = my_decorator(my_func)>> my_func()#outputBefore the function runsI am a stand alone functionAfter the function runsmy_decorator(my_func)返回了wrapper()函数,所以,my_func其实变成了wrapper的一个变量,而后面的my_func()执行其实变成了wrapper()比如:多个decorator@decorator_one@decorator_twodef func(): pass 相当于:func = decorator_one(decorator_two(func))比如:带参数的decorator:@decorator(arg1, arg2)def func(): pass相当于:func = decorator(arg1,arg2)(func)带参数的装饰器首先看一下, 如果被装饰的方法有参数def a_decorator(method_to_decorate): def wrapper(self, x): x -= 3 print 'x is %s' % x method_to_decorate(self, x) return wrapperclass A(object): def __init__(self): self.b = 42 @a_decorator def number(self, x): print "b is %s" % (self.b + x)a = A()a.number(-3)# outputx is -6b is 36通常我们都使用更加通用的装饰器,可以作用在任何函数或对象方法上,而不必关系其参数 使用def a_decorator(method_to_decorate): def wrapper(*args, **kwargs): print '****** args ******' print args print kwargs method_to_decorate(*args, **kwargs) return wrapper @a_decoratordef func(): pass func()#output****** args ******(){}@a_decoratordef func_with_args(a, b=0): pass return a + bfunc_with_args(1, b=2)#output****** args ******(1,){'b': 2}上边的示例是带参数的被装饰函数现在我们看一下向装饰器本身传递参数向装饰器本身传递参数再看一下这段代码def deco(fn): print "I am %s!" % fn.__name__ @decodef func(): pass # outputI am func!没有执行func 函数 但是 deco 被执行了在用某个 at deco来修饰某个函数func时其解释器会解释成下面这样的语句:func = deco(func)装饰器必须使用函数作为参数,你不能直接传递参数给装饰器本身如果想传递参数给装饰器,可以声明一个用于创建装饰器的函数# 我是一个创建装饰器的函数def decorator_maker(): print "I make decorators!" def my_decorator(func): print "I am a decorator!" def wrapped(): print "I am the wrapper around the decorated function. " return func() print "As the decorator, I return the wrapped function." return wrapped print "As a decorator maker, I return a decorator" return my_decorator# decorator_maker()返回的是一个装饰器new_deco = decorator_maker()#outputsI make decorators!As a decorator maker, I return a decorator# 使用装饰器def decorated_function(): print "I am the decorated function"decorated_function = new_deco(decorated_function) decorated_function()# outputsI make decorators!As a decorator maker, I return a decoratorI am a decorator!As the decorator, I return the wrapped function.I am the wrapper around the decorated function. I am the decorated function**使用 at 修饰**decorated_function = new_deco(decorated_function)# 等价于下面的方法@new_decodef func(): print "I am the decorated function"@decorator_maker()def func(): print "I am the decorated function"my_decorator(装饰器函数)是decorator_maker(装饰器生成函数)的内部函数所以可以使用把参数加在decorator_maker(装饰器生成函数)的方法像装饰器传递参数# 我是一个创建带参数装饰器的函数def decorator_maker_with_arguments(darg1, darg2): print "I make decorators! And I accept arguments:", darg1, darg2 def my_decorator(func): print "I am a decorator! Somehow you passed me arguments:", darg1, darg2 def wrapped(farg1, farg2): print "I am the wrapper around the decorated function." print "I canaccessall the variables", darg1, darg2, farg1, farg2

return func(farg1, farg2)

print "As the decorator, I return the wrapped function."

return wrapped

print "As a decorator maker, I return a decorator"

return my_decorator

@decorator_maker_with_arguments("deco_arg1", "deco_arg2")

def decorated_function_with_arguments(function_arg1, function_arg2):

print ("I am the decorated function and only knows about my arguments: {0}"

" {1}".format(function_arg1, function_arg2))

decorated_function_with_arguments('farg1', 'farg2')

# outputs

I make decorators! And I accept arguments: deco_arg1 deco_arg2

As a decorator maker, I return a decorator

I am a decorator! Somehow you passed me arguments: deco_arg1 deco_arg2

As the decorator, I return the wrapped function.

I am the wrapper around the decorated function.

I can access all the variables deco_arg1 deco_arg2 farg1 farg2

I am the decorated function and only knows about my arguments: farg1 farg2

这里装饰器生成函数内部传递参数是闭包的特性

使用装饰器需要注意装饰器是Python2.4的新特性

装饰器会降低代码的性能

装饰器仅在Python代码导入时被调用一次,之后你不能动态地改变参数.当你使用"import x",函数已经被装饰最后Python2.5解决了最后一个问题,它提供functools模块,包含functools.wraps.这个函数会将被装饰函数的名称,模块,文档字符串拷贝给封装函数

def foo():

print "foo"

print foo.__name__

#outputs: foo

# 但当你使用装饰器

def bar(func):

def wrapper():

print "bar"

return func()

return wrapper

@bar

def foo():

print "foo"

print foo.__name__

#outputs: wrapper

"functools" 可以修正这个错误

import functools

def bar(func):

# 我们所说的 "wrapper", 封装 "func"

@functools.wraps(func)

def wrapper():

print "bar"

return func()

return wrapper

@bar

def foo():

print "foo"

# 得到的是原始的名称, 而不是封装器的名称

print foo.__name__

#outputs: foo

类装饰器class myDecorator(object):

def __init__(self, func):

print "inside myDecorator.__init__()"

self.func = func

def __call__(self):

self.func()

print "inside myDecorator.__call__()"

@myDecorator

def aFunction():

print "inside aFunction()"

print "Finished decorating aFunction()"

aFunction()

# output:

# inside myDecorator.__init__()

# Finished decorating aFunction()

# inside aFunction()

# inside myDecorator.__call__()

我们可以看到这个类中有两个成员:

一个是__init__(),这个方法是在我们给某个函数decorator时被调用,所以,需要有一个func的参数,也就是被decorator的函数。

一个是__call__(),这个方法是在我们调用被decorator函数时被调用的**如果decorator有参数的话,init() 就不能传入func了,而fn是在__call__的时候传入**

class myDecorator(object):

def __init__(self, arg1, arg2):

self.arg1 = arg2

def __call__(self, func):

def wrapped(*args, **kwargs):

return self.func(*args, **kwargs)

return wrapped

装饰器示例def counter(func):

"""

记录并打印一个函数的执行次数

"""

def wrapper(*args, **kwargs):

wrapper.count = wrapper.count + 1

res = func(*args, **kwargs)

print "{0} has been used: {1}x".format(func.__name__, wrapper.count)

return res

wrapper.count = 0

return wrapper

装饰器做缓存from functools import wraps

def memo(fn):

cache = {}

miss = object()

@wraps(fn)

def wrapper(*args):

result = cache.get(args, miss)

if result is miss:

result = fn(*args)

print "{0} has been used: {1}x".format(fn.__name__, wrapper.count)

cache[args] = result

return result

return wrapper

@memo

def fib(n):

if n < 2:

return n

return fib(n - 1) + fib(n - 2)

15言和知性中用到的缓存

def cache_for(duration):

def deco(func):

@wraps(func)

def fn(*args, **kwargs):

key = pickle.dumps((args, kwargs))

value, expire = func.func_dict.get(key, (None, None))

now = int(time.time())

if value is not None and expire > now:

return value

value = func(*args, **kwargs)

func.func_dict[key] = (value, int(time.time()) + duration)

return value

return fn

return deco

更多缓存示例

统计代码运行时间def timeit(fn):

@wraps(fn)

def real_fn(*args, **kwargs):

if config.common['ENVIRON'] == 'PRODUCTION':

return fn(*args, **kwargs)

_start = time.time()

#app.logger.debug('Start timeit for %s' % fn.__name__)

result = fn(*args, **kwargs)

_end = time.time()

_last = _end - _start

app.logger.debug('End timeit for %s in %s seconds.' %

(fn.__name__, _last))

return result

return real_fn

【更多debug工具 示例】

【flask route】

PythonDecoratorLibrary

关于Python Decroator的各种提案

Python本身提供了一些装饰器:property,staticmethod

参考链接How can I make a chain of function decorators in Python?

理解PYTHON中的装饰器

Python修饰器的函数式编程

Understanding Python Decorators in 12 Easy Steps!

PEP 0318 -- Decorators for Functions and Methods

PEP 3129 -- Class Decorators

 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
 
© 2005- 王朝網路 版權所有 導航