修饰符

函数是一种对象

Python 中,函数是也是一种对象。

In [1]:

  1. def foo(x):
  2. print x
  3.  
  4. print(type(foo))
  1. <type 'function'>

查看函数拥有的方法:

In [2]:

  1. dir(foo)

Out[2]:

  1. ['__call__',
  2. '__class__',
  3. '__closure__',
  4. '__code__',
  5. '__defaults__',
  6. '__delattr__',
  7. '__dict__',
  8. '__doc__',
  9. '__format__',
  10. '__get__',
  11. '__getattribute__',
  12. '__globals__',
  13. '__hash__',
  14. '__init__',
  15. '__module__',
  16. '__name__',
  17. '__new__',
  18. '__reduce__',
  19. '__reduce_ex__',
  20. '__repr__',
  21. '__setattr__',
  22. '__sizeof__',
  23. '__str__',
  24. '__subclasshook__',
  25. 'func_closure',
  26. 'func_code',
  27. 'func_defaults',
  28. 'func_dict',
  29. 'func_doc',
  30. 'func_globals',
  31. 'func_name']

在这些方法中,call 是最重要的一种方法:

In [3]:

  1. foo.__call__(42)
  1. 42

相当于:

In [4]:

  1. foo(42)
  1. 42

因为函数是对象,所以函数可以作为参数传入另一个函数:

In [5]:

  1. def bar(f, x):
  2. x += 1
  3. f(x)

In [6]:

  1. bar(foo, 4)
  1. 5

修饰符

修饰符是这样的一种函数,它接受一个函数作为输入,通常输出也是一个函数:

In [7]:

  1. def dec(f):
  2. print 'I am decorating function', id(f)
  3. return f

len 函数作为参数传入这个修饰符函数:

In [8]:

  1. declen = dec(len)
  1. I am decorating function 33716168

使用这个新生成的函数:

In [9]:

  1. declen([10,20,30])

Out[9]:

  1. 3

上面的例子中,我们仅仅返回了函数的本身,也可以利用这个函数生成一个新的函数,看一个新的例子:

In [10]:

  1. def loud(f):
  2. def new_func(*args, **kw):
  3. print 'calling with', args, kw
  4. rtn = f(*args, **kw)
  5. print 'return value is', rtn
  6. return rtn
  7. return new_func

In [11]:

  1. loudlen = loud(len)

In [12]:

  1. loudlen([10, 20, 30])
  1. calling with ([10, 20, 30],) {}
  2. return value is 3

Out[12]:

  1. 3

用 @ 来使用修饰符

Python 使用 @ 符号来将某个函数替换为修饰符之后的函数:

例如这个函数:

In [13]:

  1. def foo(x):
  2. print x
  3.  
  4. foo = dec(foo)
  1. I am decorating function 64021672

可以替换为:

In [14]:

  1. @dec
  2. def foo(x):
  3. print x
  1. I am decorating function 64021112

事实上,如果修饰符返回的是一个函数,那么可以链式的使用修饰符:

  1. @dec1
  2. @dec2
  3. def foo(x):
  4. print x

使用修饰符 loud 来定义这个函数:

In [15]:

  1. @loud
  2. def foo(x):
  3. print x

In [16]:

  1. foo(42)
  1. calling with (42,) {}
  2. 42
  3. return value is None

例子

定义两个修饰器函数,一个将原来的函数值加一,另一个乘二:

In [17]:

  1. def plus_one(f):
  2. def new_func(x):
  3. return f(x) + 1
  4. return new_func
  5.  
  6. def times_two(f):
  7. def new_func(x):
  8. return f(x) * 2
  9. return new_func

定义函数,先乘二再加一:

In [18]:

  1. @plus_one
  2. @times_two
  3. def foo(x):
  4. return int(x)

In [19]:

  1. foo(13)

Out[19]:

  1. 27

修饰器工厂

decorators factories 是返回修饰器的函数,例如:

In [20]:

  1. def super_dec(x, y, z):
  2. def dec(f):
  3. def new_func(*args, **kw):
  4. print x + y + z
  5. return f(*args, **kw)
  6. return new_func
  7. return dec

它的作用在于产生一个可以接受参数的修饰器,例如我们想将 loud 输出的内容写入一个文件去,可以这样做:

In [21]:

  1. def super_loud(filename):
  2. fp = open(filename, 'w')
  3. def loud(f):
  4. def new_func(*args, **kw):
  5. fp.write('calling with' + str(args) + str(kw))
  6. # 确保内容被写入
  7. fp.flush()
  8. fp.close()
  9. rtn = f(*args, **kw)
  10. return rtn
  11. return new_func
  12. return loud

可以这样使用这个修饰器工厂:

In [22]:

  1. @super_loud('test.txt')
  2. def foo(x):
  3. print x

调用 foo 就会在文件中写入内容:

In [23]:

  1. foo(12)
  1. 12

查看文件内容:

In [24]:

  1. with open('test.txt') as fp:
  2. print fp.read()
  1. calling with(12,){}

In [25]:

  1. import os
  2. os.remove('test.txt')

原文: https://nbviewer.jupyter.org/github/lijin-THU/notes-python/blob/master/05-advanced-python/05.12-decorators.ipynb