python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python使用Traits库

Python使用Traits库实现对象属性

投稿:yin

Python作为一种动态编程语言,它的变量没有类型,这种灵活性给快速开发带来很多便利,不过它也不是没有缺点,Traits库的一个很重要的目的就是为了解决这些缺点所带来的问题,trait为Python对象的属性增加了类型定义的功能

Python作为一种动态编程语言,它的变量没有类型,这种灵活性给快速开发带来很多便利,不过它也不是没有缺点。Traits库的一个很重要的目的就是为了解决这些缺点所带来的问题。

背景

Traits库最初是为了开发Chaco(一个2D绘图库)而设计的,绘图库中有很多绘图用的对象,每个对象都有很多例如线型、颜色、字体之类的属性。为了方便用户使用,每个属性可以允许多种形式的值。例如,颜色属性可以是'red'、0xff0000、(255, 0, 0)

也就是说可以用字符串、整数、组元等类型的值表达颜色,这样的需求初看起来用Python的无类型变量是一个很好的选择,因为我们可以把各种各样的值赋值给颜色属性。但是颜色属性虽然可以接受多样的值,却不是能接受所有的值,比如"abc"、0.5等等就不能很好地表示颜色。而且虽然为了方便用户使用,对外的接口可以接受各种各样形式的值,但是在内部必须有一个统一的表达方式来简化程序的实现。

用Trait属性可以很好地解决这样的问题:

让我们来看一下使用traits属性表示颜色的例子:

from enthought.traits.api import HasTraits, Color

class Circle(HasTraits):
    color = Color

这个程序从enthought.traits.api中导入我们需要使用的两个对象: HasTraits和Color。所有拥有trait属性的类都需要从HasTraits继承。由于Python的多继承特性,我们很容易将现有的类改为支持trait属性。Color是一个TraitFactory对象,我们在Circle类的定义中用它来声明一个color属性。

Traits是什么

trait为Python对象的属性增加了类型定义的功能,此外还提供了如下的额外功能:

下面这个简单的例子展示了trait所提供的这五项能力:

from enthought.traits.api import Delegate, HasTraits, Instance, Int, Str

class Parent ( HasTraits ):
    # 初始化: last_name被初始化为'Zhang'
    last_name = Str( 'Zhang' )

class Child ( HasTraits ):
    age = Int

    # 验证: father属性的值必须是Parent类的实例
    father = Instance( Parent )

    # 委托: Child的实例的last_name属性委托给其father属性的last_name
    last_name = Delegate( 'father' )

    # 监听: 当age属性的值被修改时,下面的函数将被运行
    def _age_changed ( self, old, new ):
        print 'Age changed from %s to %s ' % ( old, new )

下面用这两个类创建立两个实例:

>>> p = Parent()
>>> c = Child()

由于没有设置c的father属性,因此无法获得它的last_name属性:

>>> c.last_name
Traceback (most recent call last):
AttributeError: 'NoneType' object has no attribute 'last_name'

设置father属性之后,我们就可以得到c的last_name了:

>>> c.father = p
>>> c.last_name
'Zhang'

设置c的age属性将触发_age_changed方法的执行:

>>> c.age = 4
Age changed from 0 to 4

调用configure_traits:

>>> c.configure_traits()
True

弹出一个如下的对话框,用户可以通过它修改c的trait属性,

为Child类自动生成的属性修改对话框

可以看到属性按照其英文名排序,垂直排为一列。由于father属性是Parent类的实例,所以它给我们一个按钮,点此按钮出现下面的设置father对象的tratis属性的对话框

点击Child对话框中的Father按钮之后,弹出编辑father属性的对话框

在上面这个对话框中修改father的Last name,可以看到child的Last name属性也随之发生变化。

我们可以调用print_traits方法输出所有的trait属性与其值:

>>> c.print_traits()
age:       4
father:    <__main__.Parent object at 0x13B49120>
last_name: u'Zhang'

调用get方法获得一个描述对象所有trait属性的dict:

>>> c.get()
{'age': 4, 'last_name': u'Zhang', 'father': <__main__.Parent object at 0x13B49120>}

此外还可以调用set方法设置trait属性的值,set方法可以同时配置多个trait的属性:

>>> c.set(age = 6)
Age changed from 4 to 6
<__main__.Child object at 0x13B494B0>

动态添加Trait属性

前面介绍的方法都是在类的定义中声明Trait属性,在类的实例中使用Trait属性。由于Python是动态语言,因此Traits库也提供了为某个特定的实例添加Trait属性的方法。

下面的例子,直接产生HasTraits类的一个实例a, 然后调用其add_trait方法动态地为a添加一个名为x的Trait属性,其类型为Float,初始值为3.0。

>>> from enthought.traits.api import *
>>> a = HasTraits()
>>> a.add_trait("x", Float(3.0))
>>> a.x
3.0

接下来再创建一个HasTraits类的实例b,用add_trait方法为b添加一个属性a,指定其类型为HasTraits类的实例。然后把实例a赋值给实例b的属性a:b.a。

>>> b = HasTraits()
>>> b.add_trait("a", Instance(HasTraits))
>>> b.a = a

然后为实例b添加一个类型为Delegate(代理)的属性y,它是b的属性a所表示的实例的属性x的代理,即b.y是b.a.x的代理。注意我们在用Delegate声明代理时,第一个参数b的一个属性名"a",第二个参数是是此属性的属性名"x",modify=True表示可以通过b.y修改b.a.x的值。我们看到当将b.y的值改为10的时候,a.x的值也同时改变了。

>>> b.add_trait("y", Delegate("a", "x", modify=True))
>>> b.y
3.0
>>> b.y = 10
>>> a.x
10.0

Traits 的使用场景

Traits 的使用场景非常广泛,以下是一些常见的应用场景:

1. 插件系统

Traits 可以用于创建插件系统,使得我们可以为应用程序添加新的功能或模块,而无需修改现有的代码。通过定义一系列的 Traits 类,我们可以轻松地将这些模块组合起来,实现动态的功能扩展。

2. 用户界面开发

Traits 提供了一种简单而强大的方式来创建用户界面。我们可以定义各种用户界面元素的 Traits,然后将它们组合在一起以构建复杂的界面。Traits 还支持属性验证和事件通知,使得我们可以轻松地处理用户输入和交互。

3. 数据分析和科学计算

Traits 提供了一种方便的方式来处理和分析数据。我们可以定义各种数据结构的 Traits,然后使用这些 Traits 来创建和操作数据。Traits 还支持属性验证和默认值,使得数据处理更加健壮和可靠。

 总结

Traits 是一个在 Python 中用于实现对象属性的库。Traits 可以帮助我们创建可复用、健壮和可扩展的代码。在软件开发中,Traits 是一种用于描述对象属性的概念。一个 Trait 是一个可重用的代码块,它定义了一组属性和方法,这些属性和方法可以被其他对象继承和使用。Traits 可以用于创建可插拔组件、增加代码的可读性和可维护性,以及提供代码的灵活性和复用性。

阅读全文