【从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
at 0x0000000002DF4AE8>

 

嵌套:(多个装饰器时,当层入参取下一层出参)

 

>>> 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)))),尽管也可以套多个装饰器,但从阅读性上来说,有点难看。

 

posted @ 2020-07-13 23:07  Carcar019  阅读(209)  评论(0编辑  收藏  举报