赞
踩
装饰器(Decorators)是 Python 的一个重要部分。简单地说:它们是修改其他函数的功能的函数。他们有助于让我们的代码更简短,也更Pythonic(Python范儿)。
首先我们来理解下 Python 中的函数:
def hi(name="zhai"):
return "hi " + name
print(hi()) # output: 'hi zhai'
# 我们甚至可以将一个函数赋值给一个变量,比如
greet = hi
# 我们这里没有在使用小括号,因为我们并不是在调用hi函数
# 而是在将它放在greet变量里头。我们尝试运行下这个
print(greet()) # output: 'hi zhai'
# 如果我们删掉旧的hi函数,看看会发生什么!
del hi
print(hi()) # outputs: NameError
print(greet()) # outputs: 'hi zhai'
以上这些就是函数的基本知识了。我们来让你的知识更进一步。在 Python 中我们可以在一个函数中定义另一个函数:
def hi(): print("-------hi()-------") def greet(): return "-------greet()-------" def welcome(): return "-------welcome()-------" print(greet()) print(welcome()) print("now you are back in the hi() function") hi() # -------hi()------- # -------greet()------- # -------welcome()------- # now you are back in the hi() function
以上实例可以看出无论何时调用hi(), greet()和welcome()都将同时被调用。但是然reet()和welcome()函数在hi()函数之外是不能访问的,显示未定义该函数,如下:
greet()
# NameError: name 'greet' is not defined
那现在我们知道了可以在函数中定义另外的函数。也就是说:我们可以创建嵌套的函数。现在你需要再多学一点,就是函数也能返回函数。
其实并不需要在一个函数里去执行另一个函数,我们也可以将其作为输出返回出来:
def hi(name="yasoob"):
def greet():
return "now you are in the greet() function"
def welcome():
return "now you are in the welcome() function"
if name == "yasoob":
return greet
else:
return welcome
a = hi()
print(a) # <function hi.<locals>.greet at 0x000001C325A0B280>
print(a()) # now you are in the greet() function
再次看看这个代码。在 if/else 语句中我们返回 greet 和 welcome,而不是 greet() 和 welcome()。当我们把一对小括号放在后面,这个函数就会执行;然而如果不放括号在它后面,那它可以被到处传递,并且可以赋值给别的变量而不去执行它。
当我们写下 a = hi()
,hi() 会被执行,而由于 name 参数默认是 yasoob,所以函数 greet 被返回了。如果我们把语句改为 a = hi(name = "zhai")
,那么 welcome 函数将被返回。
def hi():
return "hi zhai!"
def doSomethingBeforeHi(func):
print("------在hi()之前做些工作------")
print(func())
doSomethingBeforeHi(hi) # 把hi()函数作为参数传递到doSomethingBeforeHi函数中
# ------在hi()之前做些工作------
# hi zhai!
上述例子,能让我们体会到最简单的装饰器的作用,即装饰器让你在一个函数的前后去执行代码。
我们可以用@符号
,使用一个简短的方式来生成一个被装饰的函数,如下:
def doSomethingBeforeHi(func):
print("------在hi()之前做些工作------")
return func
@doSomethingBeforeHi # 作用与hi=doSomethingBeforeHi(hi)相同
def hi():
print ("------hi()------")
# ------在hi()之前做些工作------
# 即使该方法被装饰后,也不会影响该函数的正常执行:
hi() # ------hi()------
以上内容参考@https://www.runoob.com/w3cnote/python-func-decorators.html
上述用@符号修饰函数的操作同样适用于修饰类,具体操作如下:
def deco(obj):
print("--------deco---------")
return obj
@deco # Foo = deco(Foo)
class Foo:
pass
# --------deco---------
将deco方法中的输出语句改成print("--------deco---------", obj)
可以查看出此时obj对象是Foo类对象,即输出--------deco--------- <class '__main__.Foo'>
。为了更直观的体会@deco
的作用,我们将上述代码进一步扩充,如下所示:
def deco(obj):
print("--------deco---------", obj)
obj.x = "zhang" # 会被加到Foo对象的属性字典中去
obj.y = "yi"
obj.z = "xing"
return obj
@deco # Foo = deco(Foo)
class Foo:
pass
print(Foo.__dict__) # 查看属性字典
运行结果如下:
如果想要对deco中的属性函数进行动态修改该怎么办呢?其实我们可以采用之前学过的函数嵌套的方式操作,将deco嵌套在一个可以指定参数的Typed方法内,具体代码如下所示:
def Typed(**kwargs):
def deco(obj):
print('----->', obj)
print("类名----->", obj)
return obj
print('======>', kwargs) # 首先执行该句
return deco # 再执行deco方法中的操作
@Typed(x='zhang', y='yi', z='xing')
class Foo:
pass
# ======> {'x': 'zhang', 'y': 'yi', 'z': 'xing'}
# -----> <class '__main__.Foo'>
# 类名-----> <class '__main__.Foo'>
类装饰器本质上和函数装饰器原理、作用相同,都是为其它函数增加额外的功能。但是相比于函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器可以直接依靠类内部的__call__方法
来实现,当使用 @ 形式将类装饰器附加到函数上时,就会调用类装饰器的__call__方法
。而不需要向函数装饰器那样,在装饰器函数中定义嵌套函数,来实现装饰功能。
【应用】使用类装饰器为一个函数的执行增加计时功能。
import time class Foo(): def __init__(self, func): # 初始化函数中传入函数对象的参数 self._func = func def __call__(self): # 定义__call__方法,直接实现装饰功能 start_time = time.time() self._func() end_time = time.time() print('花费了 %.2f' % (end_time - start_time)) @Foo # bar=Foo(bar) def bar(): print('bar函数的执行时间为:') time.sleep(2.5) bar()
执行bar()方法,就会自动调用Foo类内部的__call__方法
。其执行结果为:
元类属于python面向对象编程的深层魔法。python中一切皆为对象,类本身也是一个对象,当使用关键字class的时候就会创建一个对象(这里的对象指的是类而非类的实例)。
一个简单的例子帮助理解:
class Foo:
pass
f = Foo()
print(type(f)) # <class '__main__.Foo'>
print(type(Foo)) # <class 'type'>
上例可以看出 f 是由Foo这个类产生的对象,而Foo本身也是对象,它是由type类创建的。
元类是类的类,是类的模板。元类是用来控制如何创建类的,正如类是创建对象的模板一样,简单来说,元类的实例就是类。
上文我们基于python中一切皆为对象的概念分析出:我们用class关键字定义的类本身也是一个对象,负责产生该对象的类称之为元类(元类可以简称为类的类),内置的元类为type。type有一种完全不同的能力,它也能动态的创建类。type可以接受一个类的描述作为参数,然后返回一个类。
type的语法如下:
type(类名, 父类的元组(针对继承的情况,可以为空),包含属性的字典(名称和值))
创建一个简单的type元类:
MyShinyClass = type('MyShinyClass', (), {}) # 返回一个类对象
print(MyShinyClass)
# 输出:<class '__main__.MyShinyClass'>
print(MyShinyClass()) # 创建一个该类的实例
# 输出:<__main__.MyShinyClass object at 0x000001E8A7992F40>
你会发现我们使用“MyShinyClass”作为类名,并且也可以把它当做一个变量来作为类的引用。
若一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以继承type来自定义元类。
# 使用type构建Foo类 Foo = type('Foo', (), {'bar': True}) # 使用type构建FooChild类继承Foo类 FooChild = type('FooChild', (Foo,), {}) print(FooChild) # <class '__main__.FooChild'> print(FooChild.bar) # bar属性是由Foo继承而来------>输出:True def echo_bar(self): print(self.bar) FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar}) # 为Foochild类增加方法 print(hasattr(Foo, 'echo_bar')) # Foo对象不包含echo_bar方法------>输出:False print(hasattr(FooChild, 'echo_bar')) # FooChild包含echo_bar方法------>输出:True my_foo = FooChild() my_foo.echo_bar() # 输出:True
由上例可以看出,在Python中,类也是对象,我们可以动态的创建类。这就是当我们使用关键字class时Python在幕后做的事情,而这就是通过元类来实现的。
【例1】在元类中控制把自定义类的数据属性都变成大写。
分析题目:
class Mymetaclass(type): def __new__(cls, name, bases, attrs): print(name) # Chinese print(bases) # () print(attrs) # {'__module__': '__main__', '__qualname__': 'Chinese', 'country': 'China', 'tag': 'Legend of the Dragon', 'walk': <function Chinese.walk at 0x000001F21E66B310>} update_attrs = {} class Chinese(metaclass=Mymetaclass): country = 'China' tag = 'Legend of the Dragon' def walk(self): print('%s is walking' % self.name) print(Chinese.__dict__) # {'__module__': '__main__', '__dict__': <attribute '__dict__' of 'Chinese' objects>, '__weakref__': <attribute '__weakref__' of 'Chinese' objects>, '__doc__': None} # 我们想要的结果就是在Chinese类的数据字典中将country和tag这两个属性变为大写。
最终实现:
class Mymetaclass(type): def __new__(cls, name, bases, attrs): update_attrs = {} for k, v in attrs.items(): if not callable(v) and not k.startswith('__'): # 数据属性是不可以被调用且不是以__开头的 update_attrs[k.upper()] = v else: update_attrs[k] = v # 其余不变 return type.__new__(cls, name, bases, update_attrs) class Chinese(metaclass=Mymetaclass): country = 'China' tag = 'Legend of the Dragon' def walk(self): print('%s is walking' % self.name) print(Chinese.__dict__)
运行结果如下:
【例2】在元类中控制自定义的类产生的对象相关的属性全部为隐藏属性,并按一定格式修改__dict__字典。
class Mymeta(type): def __init__(self, class_name, class_bases, class_dic): # 控制类Foo的创建 super(Mymeta, self).__init__(class_name, class_bases, class_dic) def __call__(self, *args, **kwargs): # 控制Foo的调用过程,即Foo对象的产生过程 obj = self.__new__(self) # obj是类的实例对象 self.__init__(obj, *args, **kwargs) # print(obj.__dict__)----->输出为:{'name': 'egon', 'age': 18, 'sex': 'male'} obj.__dict__ = {'_%s__%s' % (self.__name__, k): v for k, v in obj.__dict__.items()} # print(obj.__dict__)----->输出为:{'_Foo__name': 'egon', '_Foo__age': 18, '_Foo__sex': 'male'} return obj class Foo(object, metaclass=Mymeta): # Foo=Mymeta(...) def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex obj = Foo('egon', 18, 'male') print(obj.__dict__) # 结果为:{'_Foo__name': 'egon', '_Foo__age': 18, '_Foo__sex': 'male'}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。