在调用之前检测方法是否被装饰
Detect if method is decorated before invoking it
我写了一个Python流程控制框架,其工作方式与unittest.TestCase
非常相似:用户创建一个从框架class派生的class,然后编写自定义task_*(self)
方法。框架发现并运行它们:
###################
# FRAMEWORK LIBRARY
###################
import functools
class SkipTask(BaseException):
pass
def skip_if(condition):
def decorator(task):
@functools.wraps(task)
def wrapper(self, *args, **kargs):
if condition(self):
raise SkipTask()
return task(self, *args, **kargs)
return wrapper
return decorator
class MyFramework(object):
def run(self):
print "Starting task"
try:
self.task()
except SkipTask:
print "Skipped task"
except Exception:
print "Failed task"
raise
else:
print "Finished task"
#############
# USER SCRIPT
#############
class MyUserClass(MyFramework):
skip_flag = True
@skip_if(lambda self: self.skip_flag)
def task(self):
print "Doing something"
if __name__ == '__main__':
MyUserClass().run()
输出:
Starting task
Skipped task
我想更改框架,以便在 @skip_if
的条件为 True
时,包装器不会打印 "Starting task"
。
我试过这个,但它不起作用:
def skip_if(condition):
def decorator(task):
print "decorating " + str(task)
task.__skip_condition = condition
return task
return decorator
class MyFramework(object):
def run(self):
try:
if self.task.__skip_condition():
print "Skipped task"
return
except AttributeError:
print str(self.task) + " is not decorated"
pass
print "Starting task"
try:
self.task()
except Exception as e:
print "Failed task: " + str(e)
raise
else:
print "Finished task"
输出:
decorating <function task at 0x194fcd70>
<bound method MyUserClass.task of <__main__.MyUserClass object at 0x195010d0>> is not decorated
Starting task
Doing something
Finished task
为什么没有跳过任务?
您正在使用双下划线名称,它在run
方法中经过了private name mangling。
单步执行调试器时,我得到:
AttributeError: "'function' object has no attribute '_MyFramework__skip_condition
这里不要使用双下划线名称;如果您将函数属性重命名为 _skip_condition
,则代码有效(前提是您绑定条件函数或显式传入 self
):
def skip_if(condition):
def decorator(task):
print "decorating " + str(task)
task._skip_condition = condition
return task
return decorator
class MyFramework(object):
def run(self):
try:
if self.task._skip_condition(self):
print "Skipped task"
return
except AttributeError:
print str(self.task) + " is not decorated"
pass
print "Starting task"
try:
self.task()
except Exception as e:
print "Failed task: " + str(e)
raise
else:
print "Finished task"
经过这些更改,输出变为:
decorating <function task at 0x1071a1b90>
Skipped task
我写了一个Python流程控制框架,其工作方式与unittest.TestCase
非常相似:用户创建一个从框架class派生的class,然后编写自定义task_*(self)
方法。框架发现并运行它们:
###################
# FRAMEWORK LIBRARY
###################
import functools
class SkipTask(BaseException):
pass
def skip_if(condition):
def decorator(task):
@functools.wraps(task)
def wrapper(self, *args, **kargs):
if condition(self):
raise SkipTask()
return task(self, *args, **kargs)
return wrapper
return decorator
class MyFramework(object):
def run(self):
print "Starting task"
try:
self.task()
except SkipTask:
print "Skipped task"
except Exception:
print "Failed task"
raise
else:
print "Finished task"
#############
# USER SCRIPT
#############
class MyUserClass(MyFramework):
skip_flag = True
@skip_if(lambda self: self.skip_flag)
def task(self):
print "Doing something"
if __name__ == '__main__':
MyUserClass().run()
输出:
Starting task
Skipped task
我想更改框架,以便在 @skip_if
的条件为 True
时,包装器不会打印 "Starting task"
。
我试过这个,但它不起作用:
def skip_if(condition):
def decorator(task):
print "decorating " + str(task)
task.__skip_condition = condition
return task
return decorator
class MyFramework(object):
def run(self):
try:
if self.task.__skip_condition():
print "Skipped task"
return
except AttributeError:
print str(self.task) + " is not decorated"
pass
print "Starting task"
try:
self.task()
except Exception as e:
print "Failed task: " + str(e)
raise
else:
print "Finished task"
输出:
decorating <function task at 0x194fcd70>
<bound method MyUserClass.task of <__main__.MyUserClass object at 0x195010d0>> is not decorated
Starting task
Doing something
Finished task
为什么没有跳过任务?
您正在使用双下划线名称,它在run
方法中经过了private name mangling。
单步执行调试器时,我得到:
AttributeError: "'function' object has no attribute '_MyFramework__skip_condition
这里不要使用双下划线名称;如果您将函数属性重命名为 _skip_condition
,则代码有效(前提是您绑定条件函数或显式传入 self
):
def skip_if(condition):
def decorator(task):
print "decorating " + str(task)
task._skip_condition = condition
return task
return decorator
class MyFramework(object):
def run(self):
try:
if self.task._skip_condition(self):
print "Skipped task"
return
except AttributeError:
print str(self.task) + " is not decorated"
pass
print "Starting task"
try:
self.task()
except Exception as e:
print "Failed task: " + str(e)
raise
else:
print "Finished task"
经过这些更改,输出变为:
decorating <function task at 0x1071a1b90>
Skipped task