你們好,這一期我想和你們分享一個OOP編程的高效神器:attrs庫python
首先咱們來介紹下 attrs 這個庫,其官方的介紹以下:git
attrs 是這樣的一個 Python 工具包,它能將你從繁綜複雜的實現上解脫出來,享受編寫 Python 類的快樂。它的目標就是在不減慢你編程速度的前提下,幫助你來編寫簡潔而又正確的代碼。github
所以用了它,定義和實現 Python 類變得更加簡潔和高效編程
首先明確一點,咱們如今是裝了 attrs 和 cattrs 這兩個庫,可是實際導入的時候是使用 attr 和 cattr 這兩個包,是不帶 s 的。app
在 attr 這個庫裏面有兩個比較經常使用的組件叫作 attrs 和 attr,前者是主要用來修飾一個自定義類的,後者是定義類裏面的一個字段的。下面是一個小例子less
from attr import attrs,attrib
@attrs
class Person:
name = attrib(type = str,default="")
age = attrib(type = int,default=0)
sex = attrib(type = str,default="")
if __name__ == '__main__':
first_person = Person("John",18,"M")
print(first_person)
Out:Person(name='John', age=18, sex='M')
複製代碼
能夠發現,Person這個類 三個屬性都只寫了一次,同時還指定了各個字段的類型和默認值,另外也不須要再定義 init 方法和 repr 方法了,很是簡潔工具
實際上,主要是 attrs 這個修飾符起了做用,而後根據定義的 attrib 屬性自動幫咱們實現了 init、repr、eq、ne、lt、le、gt、ge、hash 這幾個方法spa
如今來用實例看一下:code
first_person = Person("John",18,"M")
second_person = Person("Nancy",16,"F")
print('Equal:', first_person == second_person) #False
print('Not Equal(ne):', first_person != second_person) #True
print('Less Than(lt):', first_person.age < second_person.age) #False
print('Less or Equal(le):', first_person.age <= second_person.age) #False
print('Greater Than(gt):', first_person.age > second_person.age) #True
print('Greater or Equal(ge):', first_person.age >= second_person.age) #True
複製代碼
對於 attrib 的定義,能夠傳入各類參數,不一樣的參數對於這個類的定義有很是大的影響。對象
下面來詳細瞭解一下每一個屬性的具體參數和用法。
首先咱們用 attrs 裏面的 fields 方法能夠查看一下
from attr import attrs, attrib,fields
print(fields(Person))
(Attribute(name='name', default='', validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=<class 'str'>, converter=None, kw_only=False), Attribute(name='age', default=0, validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=<class 'int'>, converter=None, kw_only=False), Attribute(name='sex', default='', validator=None, repr=True, cmp=True, hash=None, init=True, metadata=mappingproxy({}), type=<class 'str'>, converter=None, kw_only=False))
複製代碼
參數 | 解釋 |
---|---|
name | 屬性的名字,是一個字符串類型 |
default | 屬性的默認值,若是沒有傳入初始化數據,那麼就會使用默認值,若是沒有默認值定義,那麼就是 NOTHING,即沒有默認值 |
validator | 驗證器,檢查傳入的參數是否合法 |
init | 是否參與初始化,若是爲 False,那麼這個參數不能當作類的初始化參數,默認是 True。 |
type | 類型,好比 int、str 等各類類型,默認爲 None |
metadata | 元數據,只讀性的附加數據 |
converter | 轉換器,進行一些值的處理和轉換器,增長容錯性 |
kw_only | 是否爲強制關鍵字參數,默認爲 False |
若是一個類的某些屬性不想參與初始化,好比想直接設置一個初始值,一直固定不變,咱們能夠將屬性的 init 參數設置爲 False,看一個實例:
from attr import attrs,attrib
@attrs
class Person:
name = attrib(type = str)
age = attrib(init=False)
sex = attrib(type = str)
first = Person("John","M") # Person(name='John', age=NOTHING, sex='M')
second = Person("Mike",89,"M")
#TypeError: __init__() takes 3 positional arguments but 4 were given
複製代碼
能夠發現,first沒有問題,可是second會報錯,由於age沒有參與初始化,只剩了self,age,sex
強制關鍵字是 Python 裏面的一個特性,在傳入的時候必須使用關鍵字的名字來傳入
設置了強制關鍵字參數的屬性必需要放在後面,其後面不能再有非強制關鍵字參數的屬性
咱們仍是拿同樣的例子,這回把sex的參數
from attr import attrs,attrib
@attrs
class Person:
name = attrib(type = str)
age = attrib(type = str)
sex = attrib(kw_only=True)
first = Person("John",18,sex="M")
#Person(name='John', age=18, sex='M')
複製代碼
若是初始化first時使用Person("John",18,"M")則會報錯
有時候在設置一個屬性的時候必需要知足某個條件,好比性別必需要是男或者女,不然就不合法。對於這種狀況,咱們就須要有條件來控制某些屬性不能爲非法值。
from attr import attrs, attrib
def is_valid_gender(instance, attribute, value):
if value not in ('M', 'F'):
raise ValueError(f'gender {value} is not valid')
@attrs
class Person:
name = attrib(type = str)
age = attrib(type = str)
sex = attrib(validator=is_valid_gender)
複製代碼
在這裏咱們定義了一個驗證器 Validator 方法,叫作 is_valid_gender,其中 gender 定義的時候傳入了一個參數 validator,其值就是咱們定義的 Validator 方法:
下面作了兩個實驗,一個就是正常傳入 "M",另外一個寫錯了,寫的是 "X":
first = Person("John",18,"M")
# Person(name='John', age=18, sex='M')
second = Person("Ann",29,"X")
ValueError: gender X is not valid
複製代碼
second報錯了,由於其值不是正常的性別,因此程序直接報錯終止 注意在 Validator 裏面返回 True 或 False 是沒用的,錯誤的值還會被照常複製。因此,必定要在 Validator 裏面 raise 某個錯誤。
另外 attrs 庫裏面還給咱們內置了好多 Validator,好比判斷類型,這裏若是規定age必須爲 int 類型:
age =attrib(validator=validators.instance_of(int))
複製代碼
另外 validator 參數還支持多個 Validator,好比咱們要設置既要是數字,又要小於 100,那麼能夠把幾個 Validator 放到一個列表裏面並傳入:
from attr import attrs, attrib,validators
def is_valid_gender(instance, attribute, value):
if value not in ('M', 'F'):
raise ValueError(f'gender {value} is not valid')
def is_less_than_100(instance, attribute, value):
if value > 100:
raise ValueError(f'age {value} must less than 100')
@attrs
class Person:
name = attrib(type = str)
age = attrib(validator=[validators.instance_of(int), is_less_than_100])
sex = attrib(validator=[validators.instance_of(str),is_valid_gender])
複製代碼
不少時候咱們會不當心傳入一些形式不太標準的結果,好比原本是 int 類型的 100,咱們傳入了字符串類型的 100,那這時候直接拋錯應該很差吧,因此咱們能夠設置一些轉換器來加強容錯機制,好比將字符串自動轉爲數字:
from attr import attrs, attrib,validators
def to_int(value):
try:
return int(value)
except:
return None
@attrs
class Person:
name = attrib(type = str)
age = attrib(converter=to_int)
sex = attrib(validator=validators.instance_of(str))
last_person = Person("xiaobai","35","M")
print(last_person)
Out: Person(name='xiaobai', age=35, sex='M')
複製代碼
此次我記錄了attrs 庫的用法,是參考了別人的文章,個人其餘原創文章已經放到了Github上,若是感興趣的朋友能夠去看看,連接以下: