我们应该使用装饰器还是上下文管理器来处理 Maya 中的撤消队列?
Should we use a decorator or a context manager to handle undo queues in Maya?
我知道上下文管理器和装饰器在Python中是两个完全不相关的概念,但两者都可以用来实现相同的目标。有时可能会混淆哪一个是最佳实践。
在 Maya 中,如果要将动作列表分组为撤消队列的单个元素,则需要打开和关闭块。这是非常危险的,因为如果在块打开时引发异常,它可能会完全破坏撤消队列。
假设我想在撤消块打开时执行以下代码:
def do_stuff():
print("I do stuff...")
一种方法是这样写:
cmds.undoInfo(openChunk=True)
try:
do_stuff()
finally:
cmds.undoInfo(closeChunk=True)
这显然是一次性的解决方案,不太实用。我知道我可以将它作为装饰器自动化:
def open_undo_chunk(func):
def wrapper():
cmds.undoInfo(openChunk=True)
print("chunck opened")
func()
cmds.undoInfo(closeChunk=True)
print("chunck closed")
return wrapper
@open_undo_chunk
def do_stuff():
print("I do stuff...")
do_stuff()
但另一种方法是使用上下文管理器。
class Open_undo_chunk():
def __enter__(self):
cmds.undoInfo(openChunk=True)
print("chunck opened")
return
def __exit__(self, exec_type, exec_val, traceback):
cmds.undoInfo(closeChunk=True)
print("chunck closed")
with Open_undo_chunk():
do_stuff()
哪一个是最佳做法,为什么在这种情况下?
我认为最佳实践通常归结为最适合您的风格。我并不特别认为这两种方法之间存在明显的性能差异,但也许有人可以为我们做一些简单的基准测试。
在对您的问题的非常主观的回答中,我个人更喜欢 with-statements。它表明您正在使用资源执行代码,该资源将在完成后自行处置。这通常也是您执行内置上下文的方式,例如打开文件等。
另一个额外的好处是您不需要在 with 语句中定义 运行 的方法。
但是,您可以使用 contextlib 生成上下文来节省一些精力:
import contextlib
import maya.cmds as cmds
@contextlib.contextmanager
def undoable():
'''Insert undo chunk'''
try:
cmds.undoInfo(openChunk=True)
yield
finally:
cmds.undoInfo(closeChunk=True)
# Simple example of invocation
with undoable():
print("I'm doing stuff")
cmds.polySphere(name='I_Am_Undoable')
Here's a thread with more information on the yield
statement。上面我实际上是在执行 yield None
,这意味着您不能对资源 undoable()
returns 做任何事情。例如,如果你有一个文件指针或类似的东西,你会 yield <resource>
将它发送回调用上下文。
我知道上下文管理器和装饰器在Python中是两个完全不相关的概念,但两者都可以用来实现相同的目标。有时可能会混淆哪一个是最佳实践。 在 Maya 中,如果要将动作列表分组为撤消队列的单个元素,则需要打开和关闭块。这是非常危险的,因为如果在块打开时引发异常,它可能会完全破坏撤消队列。
假设我想在撤消块打开时执行以下代码:
def do_stuff():
print("I do stuff...")
一种方法是这样写:
cmds.undoInfo(openChunk=True)
try:
do_stuff()
finally:
cmds.undoInfo(closeChunk=True)
这显然是一次性的解决方案,不太实用。我知道我可以将它作为装饰器自动化:
def open_undo_chunk(func):
def wrapper():
cmds.undoInfo(openChunk=True)
print("chunck opened")
func()
cmds.undoInfo(closeChunk=True)
print("chunck closed")
return wrapper
@open_undo_chunk
def do_stuff():
print("I do stuff...")
do_stuff()
但另一种方法是使用上下文管理器。
class Open_undo_chunk():
def __enter__(self):
cmds.undoInfo(openChunk=True)
print("chunck opened")
return
def __exit__(self, exec_type, exec_val, traceback):
cmds.undoInfo(closeChunk=True)
print("chunck closed")
with Open_undo_chunk():
do_stuff()
哪一个是最佳做法,为什么在这种情况下?
我认为最佳实践通常归结为最适合您的风格。我并不特别认为这两种方法之间存在明显的性能差异,但也许有人可以为我们做一些简单的基准测试。
在对您的问题的非常主观的回答中,我个人更喜欢 with-statements。它表明您正在使用资源执行代码,该资源将在完成后自行处置。这通常也是您执行内置上下文的方式,例如打开文件等。
另一个额外的好处是您不需要在 with 语句中定义 运行 的方法。
但是,您可以使用 contextlib 生成上下文来节省一些精力:
import contextlib
import maya.cmds as cmds
@contextlib.contextmanager
def undoable():
'''Insert undo chunk'''
try:
cmds.undoInfo(openChunk=True)
yield
finally:
cmds.undoInfo(closeChunk=True)
# Simple example of invocation
with undoable():
print("I'm doing stuff")
cmds.polySphere(name='I_Am_Undoable')
Here's a thread with more information on the yield
statement。上面我实际上是在执行 yield None
,这意味着您不能对资源 undoable()
returns 做任何事情。例如,如果你有一个文件指针或类似的东西,你会 yield <resource>
将它发送回调用上下文。