Decorator(装饰器) @符号修饰函数(有的语言称为:注释) python 2.4以后,增加了 @符号修饰函数 对函数进行修饰, python3.0/2.6又增加了对类的修饰。 修饰符必须出现在函数定义前一行,不允许和函数定义在同一行。也就是说 @A def f(): 是非法的。 简单的来说就是用一个新的对象来替换掉原有的对象,新的对象包含原有的对象,并且会处理它的执行结果和输入参数。 python另外一个很有意思的属性:可以在函数中定义函数。 其实总体说起来,装饰器其实也就是一个函数,一个用来包装函数的函数,返回一个修改之后的函数对象,将其重新赋值原来的标识符,并永久丧失对原始函数对象的访问。 官方的说明: http://www.python.org/dev/peps/pep-0318/ 代码上解释 Decorator(装饰器) 就是这样: @decomaker def func(arg1, arg2, ...): pass # 上面相当于这样写: result = decomaker(func)(arg1, arg2, ...) 范例(普通函数的用法,查看函数运行时间): import time def timeit(func): def wrapper(*args, **kwargs): start = time.time() func(*args, **kwargs) end =time.time() print( 'used:' + str(end - start) ) return wrapper @timeit # 相当于: timeit(foo)() def foo(): print( 'in foo()' ) # 调用被修饰过的函数,跟普通函数没啥区别 foo() # 打印: in foo() # 下面用一个不写“@”装饰的函数测试调用方法 def foo2(): print( 'in foo2()' ) # 不被装饰的函数,真实的修饰这样写: timeit(foo2)() # 打印: in foo2() 范例(多个修饰函数,原函数带参数): def bold(fn): def wrapped(arg): return "<b>" + fn(arg) + "</b>" return wrapped def italic(fn): def wrapped(arg): return "<i>" + fn(arg) + "</i>" return wrapped @bold # 相当于: bold(italic(hello))(arg) @italic # 相当于: italic(hello)(arg) def hello(s): return "hello " + s # 调用被修饰过的函数,跟普通函数没啥区别 print( hello('holemar') ) # 打印: <b><i>hello holemar</i></b> # 下面用一个不写“@”装饰的函数测试调用方法 def hello2(s): return "hello " + s # 不被装饰的函数,真实的修饰这样写: print( italic(hello2)('world') ) # 打印: <i>hello world</i> print( bold(hello2)('world') ) # 打印: <b>hello world</b> print( bold(italic(hello2))('world') ) # 打印: <b><i>hello world</i></b> 范例(装饰器带参数的用法,通用的处理输入和输出结果,这写法仅适用于py2.x): # 如果装饰器需要带参数,则里面需要嵌套两层函数(不带参数的只需嵌套一层),看下面的“相当于”就会明白 def accepts(*types): def check_accepts(f): assert len(types) == f.func_code.co_argcount def new_f(*args, **kwds): for (a, t) in zip(args, types): assert isinstance(a, t), "arg %r does not match %s" % (a,t) return f(*args, **kwds) new_f.func_name = f.func_name return new_f return check_accepts def returns(rtype): def check_returns(f): def new_f(*args, **kwds): result = f(*args, **kwds) assert isinstance(result, rtype), "return value %r does not match %s" % (result,rtype) return result new_f.func_name = f.func_name return new_f return check_returns # 注意这两个装饰器的顺序,如果倒过来会出错,因为 @accepts 先过滤了 func 函数,而 @returns 又过滤 @accepts 函数的结果 @returns((int,float)) # 相当于: returns((int,float)) (accepts(int, (int,float))(func2)) (arg1, arg2) @accepts(int, (int,float)) # 相当于: accepts(int, (int,float)) (func2) (arg1, arg2) def func(arg1, arg2): return arg1 * arg2 # 测试一下运行结果 print( func(2, 3) ) # 下面用一个不写“@”装饰的函数测试调用方法 def func2(arg1, arg2): return arg1 * arg2 # 下面的调用,要注意理解各个圆括号, 尤其最后一行嵌套的写法 print( returns((int,float))(func2)(2, 5) ) print( accepts(int, (int,float))(func2)(2, 5) ) print( returns((int,float)) (accepts(int, (int,float))(func2)) (2, 5) ) 内置的装饰器 有三个,分别是 staticmethod, classmethod 和 property 作用分别是把类中定义的实例方法变成静态方法、类方法和类属性。 由于模块里可以定义函数,所以静态方法和类方法的用处并不是太多,除非你想要完全的面向对象编程。 而属性也不是不可或缺的,Java没有属性也一样活得很滋润。使用频率较少,了解即可。 staticmethod 和 classmethod 的用法与区别: 对于 classmethod 的参数, 需要隐式地传递类名, 而 staticmethod 参数中则不需要传递类名, 其实这就是二者最大的区别。 对于 staticmethod 就是为了要在类中定义而设置的,一般来说很少这样使用,可以使用模块级(module-level)的函数来替代它。既然要把它定义在类中,想必有作者的考虑。 对于 classmethod, 可以通过子类来进行重定义。 范例(staticmethod 和 classmethod 的用法与区别): class Person: def sayHi(self): # self参数必须写,正常函数的写法 print('Hello, how are you?') @staticmethod # 申明此方法是一个静态方法,外部可以直接调用 def tt(a): # 静态方法,第一个参数不需要用 self print(a) # 第一个参数就是传过来的参数 def ff(self): self.sayHi() # 正常方法的调用 self.tt('dd') # 静态方法的调用 @classmethod # 申明此方法是一个类方法 def class_method(class_name, arg1): c = class_name() c.sayHi() # 正常类方法的调用,用之前需要new这个类 class_name.tt('cc') # 静态方法的调用 print(arg1) # 第一个参数是类名,第二个参数开始才是传过来的参数 p = Person() p.ff() # 正常方法的调用: self参数不需赋值, 必须先 new 出一个类才可以用 Person.tt('tt') # 可以直接调用 p.tt('tt') # 使用实例调用也行 Person.class_method('cm') # 也可以直接调用 范例(利用 classmethod 实现单例模式): class IOLoop(object): def __init__(self): print( 'IOLoop.__init__' ) @classmethod def instance(cls): if not hasattr(cls, "_instance"): cls._instance = cls() return cls._instance @classmethod def initialized(cls): """Returns true if the singleton instance has been created.""" return hasattr(cls, "_instance") def service(self): print 'Hello,World' # 下面调用一下,看看效果 print( IOLoop.initialized() ) # 打印: False 表示还没有初始化这个类 ioloop = IOLoop.instance() # 打印: IOLoop.__init__ 表示执行了这个类的 __init__ 构造函数 ioloop.service() # 打印: Hello,World print( IOLoop.initialized() ) # 打印: True 表示已经初始化这个类了 ioloop = IOLoop.instance() # 没有打印,因为 __init__ 不需要再次执行 ioloop.service() # 打印: Hello,World property 属性用法(原理可参考文档“1.6.3.属性”) 1.现在介绍第一种使用属性的方法(不使用 @property 的写法): 在该类中定义三个函数,分别用作赋值、取值和删除变量(此处表达也许不很清晰,请看示例) # 假设定义了一个类:C, 该类必须继承自object类, 有一私有变量__x class C(object): def __init__(self): self.__x=None # 取值函数 def getx(self): return self.__x # 赋值函数 def setx(self,value): self.__x=value # 删除函数 def delx(self): #del self.__x self.__x=None # 定变量名 # property 函数原型为 property(fget=None, fset=None, fdel=None, doc=None), 所以根据自己需要定义相应的函数即可。 x = property(getx, setx, delx, '属性x的doc') # 现在这个类中的x属性便已经定义好了,我们可以对它进行赋值、取值, 以及删除操作 c=C() # new 一个实例 c.x=100 # 赋值 y=c.x # 取值 print(y) # 打印: 100 print(c.x) # 打印: 100 del c.x # 删除变量 print(c.x) # 打印: None print(C.x.__doc__) # 打印这属性的doc 2.下面看第二种方法(在py2.6中新增, 使用 @property 修饰符的写法) 注意同一属性的三个函数名要相同 # 同样定义一个类:C, 该类也有一私有变量__x class C(object): def __init__(self): self.__x=None @property # 申明这是一个属性,同时也定义了取值函数、doc def x(self): '''属性x的doc ''' return self.__x @x.setter # 赋值函数 def x(self,value): self.__x=value @x.deleter # 删除函数 def x(self): #del self.__x self.__x=None # 对属性进行赋值、取值, 以及删除的操作同上例, 不再写 functools 模块提供了两个装饰器。 这个模块是Python 2.5后新增的,一般来说大家用的应该都高于这个版本。 wraps(wrapped[, assigned][, updated]): 函数是有几个特殊属性比如函数名,在被装饰后,上例中的函数名foo会变成包装函数的名字wrapper,这个装饰器可以解决这个问题,它能将装饰过的函数的特殊属性保留。 total_ordering(cls): 这个装饰器在特定的场合有一定用处,但是它是在Python 2.7后新增的。 它的作用是为实现了至少 __lt__, __le__, __gt__, __ge__ 其中一个的类加上其他的比较方法,这是一个类装饰器。 如果觉得不好理解,不妨仔细看看这个装饰器的源代码。 范例(functools.wraps 装饰器的用法与用途): import time import functools def timeit(func): @functools.wraps(func) def wrapper(): start = time.clock() func() end =time.clock() print 'used:', end - start return wrapper @timeit def foo(): print 'in foo()' foo() print(foo.__name__) # 打印: foo, 没有 @functools.wraps 装饰过的话,打印 wrapper

浙公网安备 33010602011771号