原文地址
https://www.codementor.io/python/tutorial/advanced-use-python-decorators-class-function
介绍
我写这篇文章的主要目的是介绍装饰器的高级用法。如果你对装饰器知之甚少,或者对本文讲到的知识点易混淆。我建议你复习下装饰器基础教程。
本教程的目标是介绍装饰器的一些有趣的用法。特别是怎样在类中使用装饰器,怎样给装饰器传递额外的参数。
装饰器 vs 装饰器模式
Decorator模式是一个面向对象的设计模式,它允许动态地往现有的对象添加行为。当你装饰了一个对象,在某种程度上,你是在独立于同一个类的其他实例的基础上扩展其功能。
Python装饰器不是装饰器模式的实现,它在函数、方法定义的时候添加功能,而不是在运行的时候添加。Decorator设计模式本身可以在Python中实现,因为Python是动态编程语言,所以没有必要这样做。
一个基础的装饰器
这是装饰器的最简单例子,在继续往下面阅读之前请确保理解此段代码。如果你需要更多关于此代码的解释,请复习下基础装饰器教程。
def time_this(original_function): def new_function(*args, **kwargs): import datetime before = datetime.datetime.now() x = original_function(*args, **kwargs) after = datetime.datetime.now() print("Elapsed Time = {}".format(after-before)) return x return new_function @time_this def func_a(stuff): import time time.sleep(stuff) func_a(3) # out: Elapsed Time = 0:00:03.012472
带参数的装饰器
有时候带参数的装饰器会非常有用,这种技术经常用在函数注册中。在web框架Pyramid中经常有用到,例如:
@view_config(route_name='home', renderer='templates/mytemplate.pt') def my_view(request): return {'project': 'hello decorators'}
比方说,我们有一个用户可以登录并且可以和用户交互的GUI应用程序。用户和GUI界面的交互触发事件,导致Python函数执行。假设有许多使用该图形界面的用户,他们各自的权限级别差异很大,不同的功能执行需要不同的权限。比如,考虑以下功能:
# 假设这些函数是存在的 def current_user_id(): """ this function returns the current logged in user id, if the use is not authenticated the return None """ def get_permissions(iUserId): """ returns a list of permission strings for the given user. For example ['logged_in','administrator','premium_member'] """ # 在这些函数中我们需要实现权限检查 def delete_user(iUserId): """ delete the user with the given Id. This function is only accessable to users with administrator permissions """ def new_game(): """ any logged in user can start a new game """ def premium_checkpoint(): """ save the game progress, only accessable to premium members """
一种实现这些权限检查的方式是实现多个装饰器,比如:
def requires_admin(fn): def ret_fn(*args,**kwargs): lPermissions = get_permissions(current_user_id()) if 'administrator' in lPermissions: return fn(*args,**kwargs) else: raise Exception("Not allowed") return ret_fn def requires_logged_in(fn): def ret_fn(*args,**kwargs): lPermissions = get_permissions(current_user_id()) if 'logged_in' in lPermissions: return fn(*args,**kwargs) else: raise Exception("Not allowed") return ret_fn def requires_premium_member(fn): def ret_fn(*args,**kwargs): lPermissions = get_permissions(current_user_id()) if 'premium_member' in lPermissions: return fn(*args,**kwargs) else: raise Exception("Not allowed") return ret_fn @requires_admin def delete_user(iUserId): """ delete the user with the given Id. This function is only accessable to users with administrator permissions """ @requires_logged_in def new_game(): """ any logged in user can start a new game """ @requires_premium_member def premium_checkpoint(): """ save the game progress, only accessable to premium members """
但是,这太可怕了。这需要大量的复制粘贴,每个装饰器需要一个不同的名字,如果有任何关于权限检查的改变,每个装饰器都需要修改。就没有一个装饰器把以上三个装饰器的工作都干了的吗?
为了解决此问题,我们需要一个返回装饰器的函数:
def requires_permission(sPermission): def decorator(fn): def decorated(*args,**kwargs): lPermissions = get_permissions(current_user_id()) if sPermission in lPermissions: return fn(*args,**kwargs) raise Exception("permission denied") return decorated return decorator def get_permissions(iUserId): # this is here so that the decorator doesn't throw NameErrors return ['logged_in',] def current_user_id(): #ditto on the NameErrors return 1 #and now we can decorate stuff... @requires_permission('administrator') def delete_user(iUserId): """ delete the user with the given Id. This function is only accessible to users with administrator permissions """ @requires_permission('logged_in') def new_game(): """ any logged in user can start a new game """ @requires_permission('premium_member') def premium_checkpoint(): """ save the game progress, only accessable to premium members """
尝试一下调用delete_user
,new name
和premium_checkpoint
然后看看发生了什么。
premium_checkpoint
和delete_user
产生了一个“permission denied”
的异常,new_game
执行正常。
下面是带参数装饰的一般形式,和例子的使用:
def outer_decorator(*outer_args,**outer_kwargs): def decorator(fn): def decorated(*args,**kwargs): do_something(*outer_args,**outer_kwargs) return fn(*args,**kwargs) return decorated return decorator @outer_decorator(1,2,3) def foo(a,b,c): print(a) print(b) print(c) foo()
等价于:
def decorator(fn): def decorated(*args,**kwargs): do_something(1,2,3) return fn(*args,**kwargs) return decorated return decorator @decorator def foo(a,b,c): print(a) print(b) print(c) foo()
类装饰器
装饰器不仅可以修饰函数,还可以对类进行装饰。比如说,我们有一个类,该类含有许多重要的方法,我们需要记录每一个方法执行的时间。我们可以使用上述的time_this
装饰此类:
class ImportantStuff(object): @time_this def do_stuff_1(self): pass @time_this def do_stuff_2(self): pass @time_this def do_stuff_3(self): pass
此方法可以运行正常。但是在该类中存在许多多余的代码,如果我们想建立更多的类方法并且遗忘了装饰其中的一个方法,如果我们不想装饰该类中的方法了,会发生什么样的情况呢?这可能会存在出现认为错误的空间,如果写成这样会更有好:
@time_all_class_methods class ImportantStuff: def do_stuff_1(self): pass def do_stuff_2(self): pass def do_stuff_3(self): pass
等价于:
class ImportantStuff: def do_stuff_1(self): pass def do_stuff_2(self): pass def do_stuff_3(self): pass ImportantStuff = time_all_class_methods(ImportantStuff)
那么time_all_class_methods
是怎么工作的呢?
首先,我们需要采用一个类作为参数,然后返回一个类,我们也要知道返回的类的功能应该和原始类ImportantStuff
功能一样。也就是说,我们仍然希望做重要的事情,我们希望记录下每个步骤发生的时间。我们写成这样:
def time_this(original_function): print("decorating") def new_function(*args,**kwargs): print("starting timer") import datetime before = datetime.datetime.now() x = original_function(*args,**kwargs) after = datetime.datetime.now() print("Elapsed Time = {0}".format(after-before)) return x return new_function def time_all_class_methods(Cls): class NewCls: def __init__(self,*args,**kwargs): self.oInstance = Cls(*args,**kwargs) def __getattribute__(self,s): try: x = super(NewCls,self).__getattribute__(s) except AttributeError: pass else: return x x = self.oInstance.__getattribute__(s) if type(x) == type(self.__init__): return time_this(x) else: return x return NewCls @time_all_class_methods class Foo: def a(self): print("entering a") import time time.sleep(3) print("exiting a") oF = Foo() oF.a() # out: decorating starting timer entering a exiting a Elapsed Time = 0:00:03.006767
总结
在此篇教程中,我们给大家展示了一些Python装饰器使用的技巧-我们介绍了怎么样把参数传递给装饰器,怎样装饰类。但是这仅仅是冰山一角。除了本文介绍的之外,还有其他好多装饰器的使用方法,我们甚至可以使用装饰器装饰装饰器(如果你有机会使用到它,这可能是一个做全面检查的好方法)。Python有一些内置的装饰器,比如:staticmethod
,classmethod
阅读完本文还需要学习什么呢?通常是没有比我在文章中展示的装饰器更复杂的了,如果你有兴趣学习更多关于改变类功能的方法,我建议您阅读下继承和OOP设计原则。或者你可以试试阅读一下元类。
以上就是深入了解Python装饰器的高级用法的详细内容,更多关于Python装饰器的资料请关注其它相关文章!
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新动态
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]