實際案例:
在某項目中,我們實現了一些類,並希望能像靜態類型語言那樣(C,C++,Java)對它們的實例屬性做類型檢查。
p = Person()
p.name = 'Bob' # 名字屬性必須是str
p.age = 18 # 年齡必須是int
p.height = 1.83 # 身高必須是float
要求:(1)可以對實例變量名指定類型
(2)賦予不正確類型時拋出異常
解決方案:
使用描述符來實現需要類型檢查的屬性:分別實現__get__, __set__,__delete__方法,在__set__內使用isinstance函數做類型檢查。
拓展: 靜態類型語言變量只能引用一種確定類型的對象並且不能改變。類型檢查是由編譯器在編譯階段完成的,對於Python動態類型語言來講一個變量可以引用任意類型的對象並且可以實時發生改變,也就是解釋器不能完成類型檢查,只能自己去實現。
什麼是描述符?描述符就是包含__get__, __set__,__delete__這樣方法的類,這三個方法只要包含其中一個那它就是描述符。
實例屬性就是在一個類中將另一個類的實例作為該類的一個屬性。
(1)描述符定義和訪問流程介紹
class Descriptor(object):
def __get__(self, instance, cls):
# instance用於區分使用類訪問x,還是使用實例訪問x
print('in __get__', instance, cls)
return instance.__dict__['x']
def __set__(self, instance, value):
# 在set中對於類型進行檢查
print('in __set__')
instance.__dict__['x'] = value
def __delete__(self, instance):
print('in __del__')
class A(object):
# 在類中定義一個類屬性x
x = Descriptor()
a = A()
# 會被Descriptor的__get__方法所截獲
print(a.x)
# 直接使用類A訪問類屬性,instance會被傳入None
print(A.x)
# 會被Descriptor的__set__方法所截獲
a.x = 5
# 會被Descriptor的__del__方法所截獲
del a.x
'''
通常來說在描述符這些方法當中訪問的是instance.__dict__這個字典,
也就是對於它的真正屬性進行操作。
'''
a = A()
a.x = 5
print(a.__dict__)
(2)實現使用描述符檢查實例屬性類型
class Attr(object):
def __init__(self, name, type_):
self.name = name
self.type_ = type_
def __get__(self, instance, cls):
return instance.__dict__[self.name]
def __set__(self, instance, value):
# 對字段類型做檢測
if not isinstance(value, self.type_):
raise TypeError('expected an %s' % self.type_)
instance.__dict__[self.name] = value
def __delete__(self, instance):
del instance.__dict__[self.name]
class Person(object):
# 定義一個name字段,申請描述符實例
name = Attr('name', str)
age = Attr('age', int)
height = Attr('height', float)
p = Person()
p.name = 'Bob'
print(p.name)
# age賦值字符串類型拋出異常錯誤
# p.age = '17'