【问题标题】:setattr and getattr with methods带有方法的 setattr 和 getattr
【发布时间】:2015-12-26 00:12:43
【问题描述】:

我有一个样板类,它将一些操作委托给一个参考类。它看起来像这样:

class MyClass():

    def __init__(self, someClass):            
        self.refClass = someClass

    def action1(self):
        self.refClass.action1()

    def action2(self):
        self.refClass.action2()

    def action3(self):
        self.refClass.action3()

这是 refClass:

class RefClass():

    def __init__(self):
        self.myClass = MyClass(self)

    def action1(self):
        #Stuff to execute action1

    def action2(self):
        #Stuff to execute action2

    def action3(self):
        #Stuff to execute action3

我想使用 Python 元编程来使它更优雅和可读,但我不确定如何。

我听说过setattrgetattr,我想我可以做类似的事情

class MyClass():

    def __init__(self, someClass):            
        self.refClass = someClass

    for action in ['action1', 'action2', 'action3']:
        def _delegate(self):
            getattr(self.refClass, action)()

然后我知道我需要从某个地方执行此操作,我猜:

MyClass.setattr(action, delegate)

我就是不能完全理解这个概念。我了解有关不重复代码以及使用带有函数式编程的 for 循环生成方法的基础知识,但是我不知道如何从其他地方调用这些方法。嘻嘻!

【问题讨论】:

  • 不能 RefClass 继承 MyClass 并覆盖方法吗?
  • 很可能是我的架构设计不是最好的,@Pynchia!这可能超出了问题的范围,但此代码是文本编辑器 GUI 的一部分。有视图(用于菜单、文本区域等),每个视图都由控制器类(MenuController、TextAreaController 等)控制。当从菜单触发的动作需要修改文本区域时,我将动作委托给 MainController,它负责设置整个 GUI 并处理暗示多个控制器的动作。所以我认为继承在这里不是一个可能的解决方案。

标签: python metaprogramming dry getattr setattr


【解决方案1】:

就个人而言,为了委派事情,我通常会这样做:

def delegate(prop_name, meth_name):
    def proxy(self, *args, **kwargs):
        prop = getattr(self, prop_name)
        meth = getattr(prop, meth_name)
        return meth(*args, **kwargs)
    return proxy

class MyClass(object):
    def __init__(self, someClass):
        self.refClass = someClass

    action1 = delegate('refClass', 'action1')
    action2 = delegate('refClass', 'action2')

这将创建您需要的所有委托方法:)

对于一些解释,这里的委托函数只是创建一个“代理”函数,它将充当一个类方法(参见self 参数?)并将给它的所有参数传递给带有@的引用对象的方法987654324@ 和 kwargs 参数(有关这些参数的更多信息,请参阅 *args and **kwargs?

你也可以用一个列表来创建它,但我更喜欢第一个,因为它对我来说更明确:)

class MyClass(object):
    delegated_methods = ['action1', 'action2']

    def __init__(self, someClass):
        self.refClass = someClass

        for meth_name in self.delegated_methods:
            setattr(self, meth_name, delegate('refClass', meth_name))

【讨论】:

  • 谢谢,@Koka-El-Kiwi。超级用户名!!我认为我的 Python 不足以理解您的委托函数。该功能是 MyClass 的一部分吗?我需要一些时间来调查 *args 和 **kwargs 到底是什么 0:) 让我检查一下...
  • 不不,委托函数不必在你的类中,你甚至可以将它放在“utils”模块中(或类似的东西,我把架构设计留给你:p )
  • 我刚刚编辑了我的答案,对委托函数和 *args/**kwargs 事情做了一些解释 :)
  • 感谢您的解释,现在清楚多了,我也了解了 * 和 **。我也愿意接受你的回答,但@ShadowRanger 的回答稍微容易一些。
【解决方案2】:

Python 已经包含对包含类的广义委托的支持。只需将MyClass的定义更改为:

class MyClass:

    def __init__(self, someClass):            
        self.refClass = someClass  # Note: You call this someClass, but it's actually some object, not some class in your example

    def __getattr__(self, name):
        return getattr(self.refClass, name)

定义后,只要在实例本身上找不到属性,就会在具有访问属性名称的实例上调用__getattr__。然后,您通过调用getattr 来委托给包含对象,以查找包含对象的属性并返回它。每次动态查找都会花费一点,所以如果你想避免它,你可以在__getattr__第一次请求属性时懒惰地缓存属性,这样后续访问就直接了:

def __getattr__(self, name):
     attr = getattr(self.refClass, name)
     setattr(self, name, attr)
     return attr

【讨论】:

  • 谢谢,@ShadowRanger。这正是我想要的!你说得非常对,我错误地命名了一个对象 someClass。我已将此标记为正确答案,但我还没有尝试过。明天会做第一件事,但它看起来正是我需要的。
  • 不客气。警告:如果您在 3.4 版之前的 Python 参考解释器上运行(他们finally fixed object finalization),那么您问题的设计肯定会创建一个参考周期。如果循环中涉及的 any 对象有__del__ 清理方法,那么 3.4 之前的循环垃圾收集器无法处理它,并且每次创建这样的对象时都会泄漏内存并且在失去对它的最后一个引用之前,不要显式地打破循环(通过清除属性)。
  • :O 我使用的是 Python 2.7。也许是时候在进一步考虑之前重新考虑设计了。这些是非常有价值的建议,再次感谢!
猜你喜欢
  • 1970-01-01
  • 2014-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-19
  • 2012-12-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多