在Python程序中按照下面的步驟使用Traits庫:
通常第2、3步是放在一起的,也就是說定義traits的同時定義trait屬性,在本手冊中的大部分例子都是采用這種方式:
from enthought.traits.api import HasTraits, Float
class Person(HasTraits):
weight = Float(50.0)
這段程序定義了一個從HasTraits類繼承的Person類,在其內部聲明了一個名為weight的trait屬性,其類型為浮點數,初始值為50.0。trait屬性像類的屬性一樣定義,像實例的屬性一樣使用。下面我們來看看如何使用trait屬性:
>>> joe = Person()
>>> joe.weight
50.0
>>> joe.weight = 70.5
>>> joe.weight = 70
>>> joe.weight = "89"
Traceback (most recent call last):
File "...trait_handlers.py", line 175, in error value )
TraitError: The 'weight' trait of a Person instance must be a float,
but a value of '89' <type 'str'> was specified.
由于joe是Person類的實例,因此它有一個名為weight的trait屬性,并且初始值為50.0。由于weight是使用Float聲明的,我們能將浮點數賦值給它,由于整數可以不丟失精度的轉換為浮點數,因此整數也可以賦值給它。然而,把浮點數賦值給整數trait屬性將會產生錯誤。由于字符串無法轉換為浮點數,因此賦值為字符串產生錯誤,錯誤的提示信息告訴我們它需要浮點數。
有時候我們希望trait屬性能夠自動的進行強制類型轉換,這樣我們就可以將字符串賦值給類型為float的trait屬性,省去了手工轉換的麻煩。這種強制類型轉換的trait屬性都用Casting trait聲明,所有的Casting trait都是以 C 開頭的:
from enthought.traits.api import HasTraits, CFloat
class Person(HasTraits):
cweight = CFloat(50.0)
>>> bill = Person()
>>> bill.cweight = "90"
>>> bill.cweight
90.0
>>> bill.cweight = "abc"
Traceback (most recent call last)
...
這段程序用CFloat聲明了一個強制類型轉換的trait屬性cweight。我們可以將能轉換為浮點數的字符串"90"賦值給它,但是 "abc" 這樣的字符串賦值仍然會拋出異常。 我們可以想象CFloat的內部處理:它先將傳入的值用內部函數float()進行強制類型轉換,然后把結果賦值給trait屬性。
我們也可以先單獨定義一個traits,然后用它來聲明多個類的多個trait屬性,下面是一個例子:
from enthought.traits.api import HasTraits, Range
coefficient = Range(-1.0, 1.0, 0.0)
class quadratic(HasTraits):
c2 = coefficient
c1 = coefficient
c0 = coefficient
x = Range(-100.0, 100.0, 0.0)
在這個例子中,我們需要定義多個trait屬性,其類型都為Range(具有取值范圍的浮點值),并且范圍都是-1.0到1.0,初始值為0.0。為了體現代碼重用,我們先用coefficient = Range(-1.0, 1.0, 0.0)定義了一個traits,然后在quadratic類中使用它定義三個trait屬性:c0, c1, c2。
Traits庫為Python的許多數據類型提供了預定義的trait類型。HasTraits派生的類中用trait類型名直接定義trait屬性,這個類的所有實例都將擁有一個初始化為缺省值的屬性,例如:
class Person(HasTraits):
age = Float
上面的例子為Person類定義了一個age屬性,其類型為浮點數,并且被初始化為0.0(Float的缺省值)。如果你希望用別的值初始化trait屬性的話,可以把這個值當作參數傳遞給trait類型:
age = Float(10.0)
幾乎所有的trait類型都是可以帶括號調用的,它可以接受缺省值或者其它的參數;還可以通過關鍵字參數接受元數據。 .. TODO:: 插入元數據鏈接
對于每個Python的簡單數據類型都對應兩種trait類型:強制類型和自動轉換類型。它們的區別在于:
| 強制型Trait | 自動型Trait | Python對應的數據類型 | 內置缺省值 | 自動轉換函數 |
|---|---|---|---|---|
| Bool | CBool | Boolean | False | bool() |
| Complex | CComplex | Complex number | 0+0j | complex() |
| Float | CFloat | Floating point number | 0.0 | float() |
| Int | CInt | Plain integer | 0 | int() |
| Long | CLong | Long integer | 0L | int() |
| Str | CStr | String | '' | str() |
| Unicode | CUnicode | Unicode | u'' | unicode() |
下面的例子演示了強制型Trait和自動型Trait之間的區別:
>>> from enthought.traits.api import HasTraits, Float, CFloat
>>> class Person ( HasTraits ):
... weight = Float
... cweight = CFloat
>>> bill = Person()
>>> bill.weight = 180 # OK, 整數和浮點數匹配(轉換為浮點數而不丟失信息)
>>> bill.cweight = 180 # OK,
>>> bill.weight = '180' # Error, 字符串和浮點數不匹配
>>> bill.cweight = '180' # OK, 調用float('180')轉換為浮點
>>> print bill.cweight
180.0
除了簡單類型以外,Traits庫還定義了許多其他的常用的數據類型。幾乎所有的Trait類型都可以直接使用其名稱或者當作函數調用,并且可以接受多種參數。
Any : 任何對象;
Any( [value = None, **metadata] )
Array : numpy的數組;
Array( [dtype = None, shape = None, value = None, typecode = None, **metadata] )
Button : 按鈕類型,通常用來觸發事件,參數都是用來描述界面中的按鈕的樣式;
Button( [label ="", image = None, style = "button", orientation = "vertical", width_padding = 7, height_padding = 5, **metadata] )
Callable : 可調用對象;
Callable( [value = None, **metadata] )
CArray : 可自動轉換類型的numpy數組; 調用的參數和Array相同
Class : Python老式類;
Class( [value, **metadata] )
Code : 某種編程語言的字符串;
Code( [value = "", minlen = 0, maxlen = sys.maxint, regex = "", **metadata] )
Color : 界面庫中所采用的顏色對象;
Color( [*args, **metadata] )
CSet : 自動轉換類型的集合對象;
CSet( [trait = None, value = None, items = True, **metadata] )
Constant : 常量對象,其值不能改變,必須指定初始值;
Constant( value*[, ***metadata] )
Dict : 字典對象,系統還定義了一系列關鍵字為字符串的字典trait類型:DictStrAny, DictStrBool, ...;
Dict( [key_trait = None, value_trait = None, value = None, items = True, **metadata] )
Directory : 表示某個目錄的路徑的字符串;
Directory( [value ="", auto_set = False, entries = 10, exists = False, **metadata] )
Disallow : 表示不容許任何值,在帶有通配符的trait屬性定義中使用
Either : 多個trait類型的復合對象,例如 Either(Str, Float) 表示其定義的屬性類型可以是字符串或者浮點數;
Either( val1*[, *val2, ..., valN, **metadata] )
Enum : 枚舉數據,其值可以是候選值中的一個;
Enum( values*[, ***metadata] )
Event : 觸發事件用的對象;
Event( [trait = None, **metadata] )
Expression : Python的表達式對象;
Expression( [value ="0", **metadata] )
File : 表示包括路徑的文件名的字符串;
File( [value = "", filter = None, auto_set = False, entries = 10, exists = False, **metadata ] )
Font : 界面庫中表示字體的對象;
Font( [*args, **metadata] )
在預定義的traits中,This和self需要單獨說明一下。它們所定義的屬性必須是包含此屬性的類(或派生類)的對象。This的缺省值為None,而self的缺省值則是包含屬性的對象本身。
下面來看一個例子:
class Employee(HasTraits):
manager = self
定義了一個Employee類,它有一個manager屬性,其類型為Employee,缺省值為對象本身:
>>> e = Employee()
>>> e.manager
<__main__.Employee object at 0x05DB72A0>
>>> e
<__main__.Employee object at 0x05DB72A0>
如果用This定義的話,那么缺省值為None。
一般來說,屬性為某個類的實例時可以這樣定義:
manager = Instance(Empolyee)
但是對于這個例子中,在定義manager屬性時,Empolyee還不存在,因此無法如此定義。如果你喜歡這種方式的話,可以用Instance("Empolyee")來定義,當兩個類的屬性交叉引用時,可以使用這種字符串的方式來定義。
This和self不但可以表示類本身,還可以表示派生的類:
>>> from enthought.traits.api import HasTraits, This
>>> class Employee(HasTraits):
... manager = This
...
>>> class Executive(Employee):
... pass
...
>>> fred = Employee()
>>> mary = Executive()
>>> fred.manager = mary
>>> mary.manager = fred
使用Enum可以定義枚舉類型,在Enum的定義中給出所有可能的值,這些值必須是Python的簡單數據類型,例如字符串、整數、浮點數等等,各個可能的值的類型可以不一樣。可以直接將可能的值作為參數,或者將其包在某個list中,第一個值為缺省值:
class Items(HasTraits):
count = Enum(None, 0, 1, 2, 3, "many")
# 或者:
# count = Enum([None, 0, 1, 2, 3, "many"])
下面是運行結果:
>>> item = Items()
>>> item.count = 2
>>> item.count = "many"
>>> item.count = 5
如果你希望候選值是可以變化的話,可以用values關鍵字指定定義侯選值的屬性名:
class Items(HasTraits):
count_list = List([None, 0, 1, 2, 3, "many"])
count = Enum(values="count_list")
我們定義一個count_list列表,然后在Enum定義中用values關鍵字指定候選值為count_list屬性。
>>> item = Items()
>>> item.count = 5
Traceback (most recent call last)
#... 略去錯誤提示,此錯誤提示無法顯示侯選值列表
>>> item.count_list.append(5)
>>> item.count = 5
>>> item.count
5
Trait對象可以有元數據屬性,這些屬性保存在HasTraits對象的trait字典中,為了解釋什么是trait字典和元數據,讓我們先來看一個例子:
from enthought.traits.api import *
class MetadataTest(HasTraits):
i = Int(99)
s = Str("test", desc="a string trait property")
test = MetadataTest()
在IPython中運行了上面的程序之后,我們對test進行如下操作:
>>> test.traits() {'i': <enthought.traits.traits.CTrait object at 0x05D44EA0>, 'trait_added': <enthought.traits.traits.CTrait object at 0x05D17CE8>, 's': <enthought.traits.traits.CTrait object at 0x05D44EF8>, 'trait_modified': <enthought.traits.traits.CTrait object at 0x05D17C90>}>>> test.trait("i") <enthought.traits.traits.CTrait object at 0x05D44EA0>>>> test.trait("s").desc 'a string trait property'
通過調用HasTraits對象的traits方法可以得到一個包含其所有trait對象的字典。請注意,trait屬性和trait對象是兩個東西:
也就是說對于每一個trait屬性都有一個與之對應的trait對象描述它。而元數據就是保存在trait對象中的額外的描述屬性用的數據。我們看到test的trait對象除了i和s之外,還有trait_added和trait_modified,著兩個在HasTraits類中定義。
元數據可以分為三類:
下面的這些元數據屬性在Traits庫內部使用,用戶可以讀取它們的值。
array : 是否是數組,不是數組的trait對象沒有此屬性
default : 對應的trait屬性的缺省值。也就是說: trait屬性的缺省值是保存在與其對應的trait對象的元數據屬性default中的:
>>> test.trait("i").default
99
default_kind : 一個描述缺省值的類型的字符串,其值可以是 value, list, dict, self, factory, method等:
>>> test.trait("i").default_kind
'value'
inner_traits : 內部的trait對象,在List, Dict等中使用,表示List和Dict內部對象的類型
trait_type : 描述trait屬性的數據類型的對象。下面的例子中,得到的就是定義trait屬性時所用的Int類的對象:
>>> test.trait("i").trait_type
<enthought.traits.trait_types.Int object at 0x05DBD2D0>
type : trait屬性的分類,可以是constant, delegate, event, property, trait
>>> test.trait("i").type
'trait'
下面的元數據屬性不是預定義的,但是可以被HasTraits對象使用: