python - 装饰器来代替重复的面向对象代码?

标签 python decorator pyside

我正在使用 Qt/PySide 开发一个 GUI,其中包含许多处理各种小部件的单独类。每个小部件管理按钮和其他用户输入之间的信号。我发现自己必须重用代码来在方法函数开始时阻止小部件信号,然后在最后释放信号。我决定尝试编写一个通用装饰器来为我做这件事。

我已经搜索过SO,并尝试在使用装饰器的经验很少的情况下尽我所能地实现这一点,所以我对我的解决方案不满意。

我的问题是,编写一个可以访问和运行该类中遵循明确格式的方法的通用装饰器的最佳方法是什么?我的方法到底是不是一个好方法?

为了清楚起见,以下是我的代码与重复代码的样子(为了简洁起见,我删除了一些代码):

class WidgetController(...):

    def __init__(...):
       self.widget.myWidget.currentIndexChanged.connect(reactToChange)

    def reactToChange(...):
        self.widget.myWidget.blockSignals(True) # Repetetive line...
        ...
        self.widget.myWidget.blockSignals(False)

    def anotherFunction(...):
        self.widget.anotherWidget.blockSignals(True)
        ...
        self.widget.anotherWidget.blockSignals(False)

我想要类似以下的内容:

class WidgetController(...):

   @blockSignals(myWidget)
   def reactToChange(...):
      ...

   @blockSignals(anotherWidget, alsoBlockThisWidget)
   def anotherFunction(...):
      ...

我开发了一个装饰器(在 hereherehere 的帮助下),但我犹豫是否要展示它,因为它感觉非常笨重。它使用 self ,我不明白它以及嵌套函数中的 exec ,并且需要将小部件名称作为字符串传递,但它似乎确实有效。这是:

class BlockSignals(object):

    def __init__(self, *toBeBlocked):
        self.toBeBlocked = toBeBlocked

    def __call__(self, f):
        toBeBlocked = self.toBeBlocked
        def wrapped_f(self, *args):
            for b in toBeBlocked:
                exec 'self.widget.' + b + '.blockSignals(False)' in locals()
            f(self, *args)
            for b in toBeBlocked:
                exec 'self.widget.' + b + '.blockSignals(False)' in locals()
        return wrapped_f

用法:

class WidgetController(...):

   @BlockSignals("myWidget")
   def reactToChange(...):
      ...

   @BlockSignals("anotherWidget", "alsoBlockThisWidget")
   def anotherFunction(...):

正如你所看到的,它并不漂亮。我希望能够摆脱字符串解析,摆脱 exec,理清令人困惑的 self 并能够通过传递来实现它它是实际的小部件对象@BlockSignals(self.widget.myWidget)。不幸的是我已经达到了我的能力极限,有人能帮忙吗?

最佳答案

您正在寻找getattr :

import functools

def blockSignals(*widgetnames):
    def decorator(func):
        @functool.wraps(func)
        def method(self, *args, **kwargs):
            widgets = [getattr(self.widget, name) for name in widgetnames]
            for widget in widgets:
                widget.blockSignals(True) 
            result = func(self, *args, **kwargs)
            for widget in widgets:
                widget.blockSignals(False)
            return result
        return method
    return decorator

class WidgetController(...):

    def __init__(...):
       self.widget.myWidget.currentIndexChanged.connect(reactToChange)

    @blockSignals('myWidget')
    def reactToChange(...):
        ...

    @blockSignals('anotherWidget', 'alsoBlockThisWidget')
    def anotherFunction(...):
        ...

您必须传递小部件的名称,而不是小部件本身,因为这些方法是在定义时定义的,而不是在定义时定义的>instance 被实例化。实例 selfself.widget 中的实际小部件在类定义时不存在。

functools.wraps装饰器将原始函数的名称及其文档字符串复制到装饰函数中。

关于python - 装饰器来代替重复的面向对象代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17146510/

相关文章:

Python PyInstaller 和包含窗口图标

python - 如何使用 python 的性别化包来处理大数据集?

python - 一个简单的Python部署问题-痛苦的世界

python - 标准化的 numpy/scipy `asarray` 装饰器

python - 如何在 PySide 中发出以 self 作为参数的信号?

python - python 2.7 的 mod_python

python - 在 Python 中包装生成器函数

python - PySide 在工作线程中等待来自主线程的信号

qt - 如何使 PySide 窗口在所有其他窗口上方弹出

python - 包装器方法的意义是什么?