【从C#走进Python】四、装饰器
Python装饰器,是指在方法上面写的@开头的一行,或者说@装饰器名。
我看廖雪峰的python教程,可以说看得不太明白,每个人的讲述方式并不一定就适合其他人,或者说,师傅领进门,修行看个人。
Python特性这本书,就比较合我胃口,在装饰器上说得很明白,@是个语法糖。
Python装饰器 |
|
语法糖本质 |
|
格式:在方法定义上一行,@开头加函数名 作用:如果方法上方添加了装饰器,那么在执行函数的时候,会把函数结果作为入参进入装饰器的(方法)内容中。 |
|
>>> def testDecorate(func): ... print('--------') ... func() ... print('--------') >>> @testDecorate ... def yell(): ... print('hello'); ... -------- hello --------
|
等价于: >>> def testDecorate(func): ... print('--------') ... func() ... print('--------') >>> def yell(): ... print('hello'); ... >>> testDecorate(yell) -------- hello --------
|
闭包: |
|
>>> def testDecorate0(func): ... def wrapper(): ... print('--------') ... func() ... print('--------') ... return wrapper ... >>> @testDecorate0 ... def yell(): ... print('hello') ... >>> yell() -------- hello --------
|
看不见被包装的函数名字 >>> yell <function testDecorate0.<locals>.wrapper
|
嵌套:(多个装饰器时,当层入参取下一层出参) |
|
>>> def upperDecorate(func): ... def wrapper(): ... return func().upper() ... return wrapper ... >>> def printDecorate(func): ... def wrapper(): ... print(func()) ... return wrapper ... ... >>> @testDecorate0 ... @printDecorate ... @upperDecorate ... def yell(): ... return 'hello' ... >>> yell() -------- HELLO --------
|
等价于: >>> def yell(): ... return 'hello' ... >>> decorated = testDecorate0( printDecorate(upperDecorate(yell))) >>> decorated() -------- HELLO --------
|
这种设计感觉有些危险,毕竟我不知道下一层的出参是什么,想不到很好地使用场景。 |
|
@property |
|
>>> class test: ... def __init__(self): ... self.name = 'test' ... @property ... def name1(self): ... return self.name ... @name1.setter ... def name2(self, value): ... self.name = value ... >>> class test0(): ... def __init__(self): ... self.name = 'test0' ... >>> t0 = test0() >>> t0.name 'test0' >>> t = test() >>> t.name1 = 1 Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: can't set attribute >>> t.name = 2 >>> t.name1 2
|
是可以直接写个get方法和set方法, 但在python编程上习惯不是这样, pythonic一些的做法是使用内置的@property装饰器。
这个装饰器是使方法变成属性来访问, 相当于一个get方法; 同时可以设置set方法来限制赋值。
下图有个使用的示例: |
@staticmethod |
|
类方法可以使用@classmethod装饰, 静态方法使用@staticmethod修饰, 注意的是,类方法需要传入cls(解释器认为首个入参就是cls类对象) 静态方法的入参就随需求了,它与类的其他所有内容都没有关系。 |
|
>>> class test01: ... def __init__(self, name): ... self.name = name ... @staticmethod ... def staticMethod(): ... print('staticMethod called') ... @classmethod ... def classMethod(cls): ... print(f'classMethod is called, {cls}') ... return cls('cat') ... >>> dir(test01) [... 'classMethod', 'staticMethod'] >>> dir(test01('dog')) [... 'classMethod', 'name', 'staticMethod'] >>> test01.classMethod() classMethod is called, <class '__main__.test01'> <__main__.test01 object at 0x0000000002DE8EB8> >>> _.name 'cat' 注:类对象、实例对象均可调用类方法、静态方法 |
让人比较遗憾的还是链式方法(像C#的拓展方法,可以做到instance.funcA().funcB().funC(),实例作为.后方法的入参,匹配到实例方法、或类方法,或程序集上的所有静态方法),但是Python没有这种方式,只能“套娃”(像funcC (funcB(funcA(instance)))),尽管也可以套多个装饰器,但从阅读性上来说,有点难看。