1.屬性
屬性定義:
python中屬性其實是普通方法的衍生
屬性操作:
用@property裝飾器操作類屬性
用類或實例直接操作類屬性obj.name,obj.age=18,del obj.age
用python內置函數操作屬性
屬性意義:
訪問屬性時類似訪問字段象,屬性由方法衍生而來,如Python沒有屬性,方法完全可代替其功能
可動態獲取屬性值,應用更靈活;可制定屬性規則,防隨意修改屬性值
屬性函數:
hasattr(object, name) #判斷是否包含指定的屬性或方法
getattr(object, name[,default]) #獲取對象屬性或方法;無拋AttributeError
setattr(object, name, values) #對象屬性賦值(修改或創建)
delattr(obj, name): #刪除屬性無拋AttributeError,無返回值
type.__dict__ #類屬性-類實例變量
dir #返回當前範圍的所有屬性名稱列表
內置屬性:
__dict__ : #獲取類所有信息返回字典{屬性:屬性值}
__doc__ : #類文檔字符串
__name__: #類名
__module__: #類定義所在模塊 當前模塊返回’__main__’
__bases__ : #類所有父類元組
property():
語法:property(fget=None, fset=None, fdel=None, doc=None) -> property attribute
說明:
fget 獲取屬性值方法
fset 設置屬性值方法
fdel 刪除屬性值方法
doc 屬性描述信息;如省略,會把 fget 方法的docstring 拿來用(如果有話)
注意:
類名.屬性.__doc__ #查看屬性文檔字符串Student.age.__doc__
屬性裝飾器:
用途:把方法包裝成屬性;在獲取,設置,刪除屬性時需額外做一些工作
實現getter,setter,deleter功能,對屬性的取值和賦值加以控制,提高代碼的穩定性
@property 語法糖提供比 property() 函數更簡潔直觀寫法
格式:
@property 獲取屬性值的方法,被裝飾方法的名字會被用做 屬性名
def func(self):僅有一個self參數,調用時無需括號
@屬性名.setter 裝飾的方法是設置屬性值的方法
@屬性名.deleter 裝飾的方法是刪除屬性值的方法
注意:
描述符只對實例方法有效,對於靜態方法、類方法都無法使用
不能通過在__init__()和其他方法中創建描述符對象來爲每個實例創建描述符
2.實例:屬性操作
實例1:#用類或實例直接操作類屬性
#實例.屬性; 類.屬性 對類的屬性沒有操作控制規則,容易被修改
class Student:
total = 10
def __init__(self,name='Tom',age=22):
self.name = name
self.age=age
def view(self):
print(self.name,self.age)
def add(self,x,y):
return x+y
#屬性操作:查看賦值刪除
s=Student()
hasattr(s, 'age')# True
s.age # 等價:getattr(s,'age') 查找屬性調用obj.__getattrribute__(‘name’)
del s.age # 等價:delattr(s,'age') 刪除屬性調用obj.__delattr__(‘name’)
s.age=20 # 等價:setattr(s,'age',20) 設置屬性調用obj.__setattr__(‘name’,value)
實例2:內置函數操作屬性
getattr(s,'age')
delattr(s,'age')
setattr(s,'age',20)
實例3:屬性裝飾器
實例3.1:
class Person(object):
def __init__(self):
self._age=None
def get_age(self):
return self.age
def set_age(self, value):
if value <= 0 or value > 200:raise ValueError('age must between 0 ~ 200!')
self.age = value
s = Person()
s.set_age(30);print(s.get_age(),end=',')
del s.age;print(hasattr(s, 'age')) # 30,False
實例3.2:
class Person(): #新式類
def __init__(self):
self._age=None
@property #getter方法變成屬性@property等同age = property(fget=age)
def age(self):
return self._age
@age.setter #把一個setter方法變成屬性賦值
def age(self, value):
if value <=0 or value > 200:raise ValueError('age must between 0 ~ 200!')
self._age = value
@age.deleter # 刪除屬性
def age(self):
del self._age
s = Person()
s.age = 30 # 實際轉化爲s.set_age(60)
print(s.age,end=',') # 實際轉化爲s.get_age()
del s.age;print(hasattr(s, 'age')) # 30,False
實例3.3:#舊版定義-不建議使用直接用@property
class Person(object):
def __init__(self):
self._age=None
def get_age(self):
return self._age
def set_age(self,value):
if value <=0 or value > 200:raise ValueError('age must between 0 ~ 200!')
self._age=value
def del_age(self):
del self._age# raise TypeError("Can't delete age")
age=property(get_age,set_age,del_age,'文檔')#增加property類
s = Person()
s.age = 30;print(s.age,end=',')
del s.age;print(hasattr(s, 'age')) # 30,False
實例4:#查看類屬性
s.__dict__ # {'name': 'Tom', 'age': 20} 類實例變量
s.__dict__['id']=1020120 # 對實例的修改反應到局部__dict__屬性中
s.__dict__ # {'name': 'Tom', 'age': 20, 'id': 1020120}
s.__class__ # <class '__main__.Student'>
# __dict__ 獲取屬性列表:
Student.__dict__.keys() # dict_keys(['__module__', 'total', '__init__', 'view', 'add', '__dict__', '__weakref__', '__doc__'])
s.__dir__() # 等價dir(s) ['name','age','id','total', 'view','add',...]
dir(Student) # [ 'add', 'total','view',...]
Student.__dir__(Student) # [ 'mro',...]
# 使用 inspect 包功能來過濾:
import inspect
#獲取屬性
[i for i in dir(s) if not callable(getattr(s, i)) and i[0]!='_']# ['age', 'id', 'name', 'total']
#獲取內置函數
[i for i in dir(s) if inspect.isbuiltin(getattr(s, i))] # ['__dir__','__format__',...]
# 獲取函數:
[i for i in dir(s) if inspect.isfunction(getattr(s, i))] # []
# 獲取可調用對象:
[i for i in dir(s) if inspect.isroutine(getattr(s, i)) and i[0]!='_']# ['add', 'view']
實例5:備註
實例5.1:特性的繼承--不建議使用
實例5.1.1:完全重寫父類property
#在子類中重新定義一個同名getter函數,再加上@propety裝飾器即可
class Student(Person):
def __init__(self):
self._age=None
@property
def age(self):
return self.age
實例5.2.2:重寫父類property的某些方法
class Girl(Person):
def __init__(self):
self._age=None
@property
def age(self):
return super().age
@age.setter
def age(self, value):
super(Girl, Girl).age.__set__(self, value)
實例5.2:
class PropertyTyped (object):#不建議使用-直接用@property
'''
定義一個描述符分配屬性進行類型檢查,如果嘗試刪除屬性,它將引發錯誤
'''
def __init__( self, name,type, default=None):
self.name='_'+name
self.type=type
self.default=default if default else type()
def __get__(self,instance,cls):
return getattr (instance, self.name, self. default)
def __set__(self,instance, value):
if not isinstance (value,self.type):
raise TypeError("Must be a%s"% self.type)
setattr (instance, self.name, value)
def __delete__(self, instance):
raise AttributeError ( "Can't delete attribute" )
class Boy (object):
name=PropertyTyped("name", str)
num=PropertyTyped ("num", int, 42)
b= Boy()
a= b.name #隱式調用Boy.name.__get__(f,Foo)
b.name="Tom" #調用Boy.name.__ set__(f, "Tom")
del b.name #調用Boy.name.__delete__(f)
實例5.3:#純Python等效項property():
class Property(object):
"Emulate PyProperty_Type() in Objects/descrobject.c"
def __init__(self, fget=None, fset=None, fdel=None, doc=None):
self.fget = fget
self.fset = fset
self.fdel = fdel
if doc is None and fget is not None:
doc = fget.__doc__
self.__doc__ = doc
def __get__(self, obj, objtype=None):
if obj is None:
return self
if self.fget is None:
raise AttributeError("unreadable attribute")
return self.fget(obj)
def __set__(self, obj, value):
if self.fset is None:
raise AttributeError("can't set attribute")
self.fset(obj, value)
def __delete__(self, obj):
if self.fdel is None:
raise AttributeError("can't delete attribute")
self.fdel(obj)
def getter(self, fget):
return type(self)(fget, self.fset, self.fdel, self.__doc__)
def setter(self, fset):
return type(self)(self.fget, fset, self.fdel, self.__doc__)
def deleter(self, fdel):
return type(self)(self.fget, self.fset, fdel, self.__doc__)