如何将非Traits模型封装以便与Python Traits一起使用?

2 投票
1 回答
501 浏览
提问于 2025-04-16 18:15

我想把一个不是Traits模型的类包装起来,以便可以和Python的Traits一起使用。我的目标是写一个基于Traits的用户界面,用来操作一个“外部”的模型类。这个外部模型类是通过SWIG生成的,所以我不能把enthought.traits.api.HasTraits作为它的父类(我觉得是这样,不过我可能错了)。

我目前的最佳尝试是

from enthought.traits.api import HasStrictTraits, Property, Instance

class ExternalModel():
    foo = 'foo'

class TraitsModel(HasStrictTraits):
    _e = Instance(ExternalModel)

    def __init__(self):
        self._e = ExternalModel()
        self.add_trait('foo', Property(lambda     :getattr(self._e,'foo'     ),
                                       lambda attr:setattr(self._e,'foo',attr)))

这让基于Traits的类TraitsModel有了一个可变的属性,这个属性指向包含的非Traits的ExternalModel实例。但是,TraitsModel.trait_names()并没有把'foo'报告为一个被识别的特性。

有没有什么建议可以让TraitsModel报告一个与ExternalModel关联的'foo'特性?enthought.traits.api.DelegatesTo似乎要求目标必须是一个Traits类(不过我可能没有找到正确的调用方式,这也是有可能的)。

更像MVC的方法可能是为我的ExternalModel提供一个基于Traits的视图。我一直没能弄明白如何让一个非Traits模型为基于Traits的视图服务。对此方向的建议也非常欢迎。

更新 我已经找到了如何让HasTraits作为ExternalModel的父类,使用的方法可以在http://agentzlerich.blogspot.com/2011_05_01_archive.html找到,但这似乎完全是浪费时间。显然,SWIG的魔法和Traits的魔法并不兼容。根据这个问题的要求,把ExternalModel包装在TraitsModel中似乎是最好的办法。

1 个回答

2
from enthought.traits.api import HasStrictTraits, Instance, Property

class ExternalModel(object):
    foo = 'foo'

class TraitsModel(HasStrictTraits):
    _e = Instance(ExternalModel, ExternalModel())

    def __init__(self):
        '''
        >>> wrapper = TraitsModel()
        >>> wrapper.foo
        'foo'
        >>> wrapper._e.foo = 'bar'
        >>> wrapper.foo
        'bar'
        >>> wrapper.trait_names()
        ['trait_added', '_e', 'foo', 'trait_modified']
        '''
        HasStrictTraits.__init__(self)
        for trait in (name for name in dir(self._e) if not name.startswith('__')):
            self.__class__.add_class_trait(
                trait,
                Property(
                    lambda:getattr(self._e, trait),
                    lambda attr:setattr(self._e, trait, attr)
                )
            )


if __name__ == '__main__':
    import doctest
    doctest.testmod()

一个比较稳妥的解决方案是使用 add_class_trait 这个方法,它属于 HasTraits 类。同时,可以用 dir(self._e) 来获取 ExternalModel 类的属性名称,再结合生成器表达式或列表推导式来过滤掉那些特殊的类方法名(如果你需要处理更复杂的类,使用 filter 和合适的函数会更好)。

另外:

  • ExternalModel 应该继承自 object

  • __init__ 方法应该调用 HasStrictTraits.__init__(或者用 super(HasStrictTraits, self).__init__()

  • _e 也可以在实例特性声明中作为第二个参数创建,使用 ExternalModel() 或者直接用 (),或者作为 TraitsModel 的一个方法,像这样:

    def __e_default(self): # note preceding underscore
        return ExternalModel()
    

最后,我有一个 稍微旧一点的 Enthought API 包括 Traits,这可能会很有用。

撰写回答