# 修饰符的使用 ## @classmethod 修饰符 在 `Python` 标准库中,有很多自带的修饰符,例如 `classmethod` 将一个对象方法转换了类方法: In [1]: ```py class Foo(object): @classmethod def bar(cls, x): print 'the input is', x def __init__(self): pass ``` 类方法可以通过 `类名.方法` 来调用: In [2]: ```py Foo.bar(12) ``` ```py the input is 12 ``` ## @property 修饰符 有时候,我们希望像 **Java** 一样支持 `getters` 和 `setters` 的方法,这时候就可以使用 `property` 修饰符: In [3]: ```py class Foo(object): def __init__(self, data): self.data = data @property def x(self): return self.data ``` 此时可以使用 `.x` 这个属性查看数据(不需要加上括号): In [4]: ```py foo = Foo(23) foo.x ``` Out[4]: ```py 23 ``` 这样做的好处在于,这个属性是只读的: In [5]: ```py foo.x = 1 ``` ```py --------------------------------------------------------------------------- AttributeError Traceback (most recent call last) in () ----> 1 foo.x = 1 AttributeError: can't set attribute ``` 如果想让它变成可读写,可以加上一个修饰符 `@x.setter`: In [6]: ```py class Foo(object): def __init__(self, data): self.data = data @property def x(self): return self.data @x.setter def x(self, value): self.data = value ``` In [7]: ```py foo = Foo(23) print foo.x ``` ```py 23 ``` 可以通过属性改变它的值: In [8]: ```py foo.x = 1 print foo.x ``` ```py 1 ``` ## Numpy 的 @vectorize 修饰符 `numpy` 的 `vectorize` 函数讲一个函数转换为 `ufunc`,事实上它也是一个修饰符: In [9]: ```py from numpy import vectorize, arange @vectorize def f(x): if x <= 0: return x else: return 0 f(arange(-10.0,10.0)) ``` Out[9]: ```py array([-10., -9., -8., -7., -6., -5., -4., -3., -2., -1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]) ``` ## 注册一个函数 来看这样的一个例子,定义一个类: In [10]: ```py class Registry(object): def __init__(self): self._data = {} def register(self, f, name=None): if name == None: name = f.__name__ self._data[name] = f setattr(self, name, f) ``` `register` 方法接受一个函数,将这个函数名作为属性注册到对象中。 产生该类的一个对象: In [11]: ```py registry = Registry() ``` 使用该对象的 `register` 方法作为修饰符: In [12]: ```py @registry.register def greeting(): print "hello world" ``` 这样这个函数就被注册到 `registry` 这个对象中去了: In [13]: ```py registry._data ``` Out[13]: ```py {'greeting': } ``` In [14]: ```py registry.greeting ``` Out[14]: ```py ``` [flask](flask.pocoo.org) ,一个常用的网络应用,处理 url 的机制跟这个类似。 ## 使用 @wraps 一个通常的问题在于: In [15]: ```py def logging_call(f): def wrapper(*a, **kw): print 'calling {}'.format(f.__name__) return f(*a, **kw) return wrapper @logging_call def square(x): ''' square function. ''' return x ** 2 print square.__doc__, square.__name__ ``` ```py None wrapper ``` 我们使用修饰符之后,`square` 的 `metadata` 完全丢失了,返回的函数名与函数的 `docstring` 都不对。 一个解决的方法是从 `functools` 模块导入 `wraps` 修饰符来修饰我们的修饰符: In [16]: ```py import functools def logging_call(f): @functools.wraps(f) def wrapper(*a, **kw): print 'calling {}'.format(f.__name__) return f(*a, **kw) return wrapper @logging_call def square(x): ''' square function. ''' return x ** 2 print square.__doc__, square.__name__ ``` ```py square function. square ``` 现在这个问题解决了,所以在自定义修饰符方法的时候为了避免出现不必要的麻烦,尽量使用 `wraps` 来修饰修饰符! ## Class 修饰符 与函数修饰符类似,类修饰符是这样一类函数,接受一个类作为参数,通常返回一个新的类。