Python使用Traits库实现对象属性
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属性都定义有自己的缺省值,这个缺省值用来初始化属性
- 验证:基于trait的属性都有明确的类型定义,只有满足定义的值才能赋值给属性
- 委托:trait属性的值可以委托给其他对象的属性
- 监听:trait属性的值的改变可以触发指定的函数的运行
- 可视化:拥有trait属性的对象可以很方便地提供一个用户界面交互式地改变trait属性的值
下面这个简单的例子展示了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 可以用于创建可插拔组件、增加代码的可读性和可维护性,以及提供代码的灵活性和复用性。
相关文章
Python 页面解析Beautiful Soup库的使用方法
Beautiful Soup 简称 BS4(其中 4 表示版本号)是一个 Python 中常用的页面解析库,它可以从 HTML 或 XML 文档中快速地提取指定的数据,这篇文章主要介绍了springboot 集成 docsify 实现随身文档 ,需要的朋友可以参考下2022-09-09


最新评论