類屬性和實(shí)例屬性
首先來(lái)看看類屬性和類實(shí)例的屬性在python中如何存儲(chǔ),通過(guò)__dir__方法來(lái)查看對(duì)象的屬性
>>
>
class
Test
(
object
)
:
pass
>>
>
test
=
Test
(
)
# 查看類屬性
>>
>
dir
(
Test
)
[
'__class__'
,
'__delattr__'
,
'__dict__'
,
'__doc__'
,
'__format__'
,
'__getattribute__'
,
'__hash__'
,
'__init__'
,
'__module__'
,
'__new__'
,
'__reduce__'
,
'__reduce_ex__'
,
'__repr__'
,
'__setattr__'
,
'__sizeof__'
,
'__str__'
,
'__subclasshook__'
,
'__weakref__'
]
# 查看實(shí)例屬性
>>
>
dir
(
test
)
[
'__class__'
,
'__delattr__'
,
'__dict__'
,
'__doc__'
,
'__format__'
,
'__getattribute__'
,
'__hash__'
,
'__init__'
,
'__module__'
,
'__new__'
,
'__reduce__'
,
'__reduce_ex__'
,
'__repr__'
,
'__setattr__'
,
'__sizeof__'
,
'__str__'
,
'__subclasshook__'
,
'__weakref__'
]
我們主要看一個(gè)屬性__dict__,因?yàn)?__dict__保存的對(duì)象的屬性,看下面一個(gè)例子
>>
>
class
Spring
(
object
)
:
.
.
.
season
=
"the spring of class"
.
.
.
# 查看Spring類保存的屬性
>>
>
Spring
.
__dict__
dict_proxy
(
{
'__dict__'
:
<
attribute
'__dict__'
of
'Spring'
objects
>
,
'season'
:
'the spring of class'
,
'__module__'
:
'__main__'
,
'__weakref__'
:
<
attribute
'__weakref__'
of
'Spring'
objects
>
,
'__doc__'
:
None
}
)
# 通過(guò)兩種方法訪問(wèn)類屬性
>>
>
Spring
.
__dict__
[
'season'
]
'the spring of class'
>>
>
Spring
.
season
'the spring of class'
發(fā)現(xiàn)__dict__有個(gè)’season’鍵,這就是這個(gè)類的屬性,其值就是類屬性的數(shù)據(jù).
接來(lái)看,看看它的實(shí)例屬性
>>
>
s
=
Spring
(
)
# 實(shí)例屬性的__dict__是空的
>>
>
s
.
__dict__
{
}
# 其實(shí)是指向的類屬性
>>
>
s
.
season
'the spring of class'
# 建立實(shí)例屬性
>>
>
s
.
season
=
"the spring of instance"
# 這樣,實(shí)例屬性里面就不空了。這時(shí)候建立的實(shí)例屬性和類屬性重名,并且把它覆蓋了
>>
>
s
.
__dict__
{
'season'
:
'the spring of instance'
}
>>
>
s
.
__dict__
[
'season'
]
'the spring of instance'
>>
>
s
.
season
'the spring of instance'
# 類屬性沒(méi)有受到實(shí)例屬性的影響
>>
>
Spring
.
__dict__
[
'season'
]
'the spring of class'
>>
>
Spring
.
__dict__
dict_proxy
(
{
'__dict__'
:
<
attribute
'__dict__'
of
'Spring'
objects
>
,
'season'
:
'the spring of class'
,
'__module__'
:
'__main__'
,
'__weakref__'
:
<
attribute
'__weakref__'
of
'Spring'
objects
>
,
'__doc__'
:
None
}
)
# 如果將實(shí)例屬性刪除,又會(huì)調(diào)用類屬性
>>
>
del
s
.
season
>>
>
s
.
__dict__
{
}
>>
>
s
.
season
'the spring of class'
# 自定義實(shí)例屬性,對(duì)類屬性沒(méi)有影響
>>
>
s
.
lang
=
"python"
>>
>
s
.
__dict__
{
'lang'
:
'python'
}
>>
>
s
.
__dict__
[
'lang'
]
'python'
# 修改類屬性
>>
>
Spring
.
flower
=
"peach"
>>
>
Spring
.
__dict__
dict_proxy
(
{
'__module__'
:
'__main__'
,
'flower'
:
'peach'
,
'season'
:
'the spring of class'
,
'__dict__'
:
<
attribute
'__dict__'
of
'Spring'
objects
>
,
'__weakref__'
:
<
attribute
'__weakref__'
of
'Spring'
objects
>
,
'__doc__'
:
None
}
)
>>
>
Spring
.
__dict__
[
'flower'
]
'peach'
# 實(shí)例中的__dict__并沒(méi)有變化
>>
>
s
.
__dict__
{
'lang'
:
'python'
}
# 實(shí)例中找不到flower屬性,調(diào)用類屬性
>>
>
s
.
flower
'peach'
下面看看類中包含方法,__dict__如何發(fā)生變化
# 定義類
>>
>
class
Spring
(
object
)
:
.
.
.
def
tree
(
self
,
x
)
:
.
.
.
self
.
x
=
x
.
.
.
return
self
.
x
.
.
.
# 方法tree在__dict__里面
>>
>
Spring
.
__dict__
dict_proxy
(
{
'__dict__'
:
<
attribute
'__dict__'
of
'Spring'
objects
>
,
'__weakref__'
:
<
attribute
'__weakref__'
of
'Spring'
objects
>
,
'__module__'
:
'__main__'
,
'tree'
:
<
function tree at
0xb748fdf4
>
,
'__doc__'
:
None
}
)
>>
>
Spring
.
__dict__
[
'tree'
]
<
function tree at
0xb748fdf4
>
# 建立實(shí)例,但是__dict__中沒(méi)有方法
>>
>
t
=
Spring
(
)
>>
>
t
.
__dict__
{
}
# 執(zhí)行方法
>>
>
t
.
tree
(
"xiangzhangshu"
)
'xiangzhangshu'
# 實(shí)例方法(t.tree('xiangzhangshu'))的第一個(gè)參數(shù)(self,但沒(méi)有寫(xiě)出來(lái))綁定實(shí)例 t,透過(guò) self.x 來(lái)設(shè)定值,即給 t.__dict__添加屬性值。
>>
>
t
.
__dict__
{
'x'
:
'xiangzhangshu'
}
# 如果沒(méi)有將x 賦值給 self 的屬性,而是直接 return,結(jié)果發(fā)生了變化
>>
>
class
Spring
(
object
)
:
.
.
.
def
tree
(
self
,
x
)
:
.
.
.
return
x
>>
>
s
=
Spring
(
)
>>
>
s
.
tree
(
"liushu"
)
'liushu'
>>
>
s
.
__dict__
{
}
需要理解python中的一個(gè)觀點(diǎn),一切都是對(duì)象,不管是類還是實(shí)例,都可以看成是對(duì)象,符合object.attribute ,都會(huì)有自己的屬性
使用__slots__優(yōu)化內(nèi)存使用
默認(rèn)情況下,python在各個(gè)實(shí)例中為名為_(kāi)_dict__的字典里存儲(chǔ)實(shí)例屬性,而字典會(huì)消耗大量?jī)?nèi)存(字典要使用底層散列表提升訪問(wèn)速度), 通過(guò)__slots__類屬性,在元組中存儲(chǔ)實(shí)例屬性,不用字典,從而節(jié)省大量?jī)?nèi)存
# 在類中定義__slots__屬性就是說(shuō)這個(gè)類中所有實(shí)例的屬性都在這兒了,如果幾百萬(wàn)個(gè)實(shí)例同時(shí)活動(dòng),能節(jié)省大量?jī)?nèi)存
>>
>
class
Spring
(
object
)
:
.
.
.
__slots__
=
(
"tree"
,
"flower"
)
.
.
.
# 仔細(xì)看看 dir() 的結(jié)果,還有__dict__屬性嗎?沒(méi)有了,的確沒(méi)有了。也就是說(shuō)__slots__把__dict__擠出去了,它進(jìn)入了類的屬性。
>>
>
dir
(
Spring
)
[
'__class__'
,
'__delattr__'
,
'__doc__'
,
'__format__'
,
'__getattribute__'
,
'__hash__'
,
'__init__'
,
'__module__'
,
'__new__'
,
'__reduce__'
,
'__reduce_ex__'
,
'__repr__'
,
'__setattr__'
,
'__sizeof__'
,
'__slots__'
,
'__str__'
,
'__subclasshook__'
,
'flower'
,
'tree'
]
>>
>
Spring
.
__slots__
(
'tree'
,
'flower'
)
# 實(shí)例化
>>
>
t
=
Spring
(
)
>>
>
t
.
__slots__
(
'tree'
,
'flower'
)
# 通過(guò)類賦予屬性值
>>
>
Spring
.
tree
=
"liushu"
# tree這個(gè)屬性是只讀的, 實(shí)例不能修改
>>
>
t
.
tree
=
"guangyulan"
Traceback
(
most recent call last
)
:
File
"
"
,
line
1
,
in
<
module
>
AttributeError
:
'Spring'
object
attribute
'tree'
is
read
-
only
>>
>
t
.
tree
'liushu'
# 對(duì)于用類屬性賦值的屬性,只能用來(lái)修改
>>
>
Spring
.
tree
=
"guangyulan"
>>
>
t
.
tree
'guangyulan'
# 對(duì)于沒(méi)有用類屬性賦值的屬性,可以通過(guò)實(shí)例來(lái)修改
>>
>
t
.
flower
=
"haitanghua"
>>
>
t
.
flower
'haitanghua'
# 實(shí)例屬性的值并沒(méi)有傳回到類屬性,你也可以理解為新建立了一個(gè)同名的實(shí)例屬性
>>
>
Spring
.
flower
<
member
'flower'
of
'Spring'
objects
>
# 如果再給類屬性賦值
>>
>
Spring
.
flower
=
"ziteng"
>>
>
t
.
flower
'ziteng'
如果使用的當(dāng),__slots__可以顯著節(jié)省內(nèi)存,按需要注意一下問(wèn)題
- 在類中定義__slots__之后,實(shí)例不能再有__slots__所列名稱之外的其他屬性
- 每個(gè)子類都要定義__slots__熟悉,因?yàn)榻忉屍鲿?huì)忽略繼承__slots__屬性
- 如果不把__werkref__加入__slots__,實(shí)例不能作為弱引用的目標(biāo)
屬性的魔術(shù)方法
來(lái)看幾個(gè)魔術(shù)方法
- __setattr__(self,name,value):如果要給 name 賦值,就調(diào)用這個(gè)方法。
- __getattr__(self,name):如果 name 被訪問(wèn),同時(shí)它不存在的時(shí)候,此方法被調(diào)用。
- __getattribute__(self,name):當(dāng) name被訪問(wèn)時(shí)自動(dòng)被調(diào)用(注意:這個(gè)僅能用于新式類),無(wú)論 name 是否存在,都要被調(diào)用。
- __delattr__(self,name):如果要?jiǎng)h除 name,這個(gè)方法就被調(diào)用。
>>
>
class
A
(
object
)
:
.
.
.
def
__getattr__
(
self
,
name
)
:
.
.
.
print
"You use getattr"
.
.
.
def
__setattr__
(
self
,
name
,
value
)
:
.
.
.
print
"You use setattr"
.
.
.
self
.
__dict__
[
name
]
=
value
# a.x,按照本節(jié)開(kāi)頭的例子,是要報(bào)錯(cuò)的。但是,由于在這里使用了__getattr__(self, name) 方法,當(dāng)發(fā)現(xiàn) x 不存在于對(duì)象的__dict__中的時(shí)候,就調(diào)用了__getattr__,即所謂“攔截成員”。
>>
>
a
=
A
(
)
>>
>
a
.
x
You use
getattr
# 給對(duì)象的屬性賦值時(shí)候,調(diào)用了__setattr__(self, name, value)方法,這個(gè)方法中有一句 self.__dict__[name] = value,通過(guò)這個(gè)語(yǔ)句,就將屬性和數(shù)據(jù)保存到了對(duì)象的__dict__中
>>
>
a
.
x
=
7
You use
setattr
# 測(cè)試__getattribute__(self,name)
>>
>
class
B
(
object
)
:
.
.
.
def
__getattribute__
(
self
,
name
)
:
.
.
.
print
"you are useing getattribute"
.
.
.
return
object
.
__getattribute__
(
self
,
name
)
# 返回的內(nèi)容用的是 return object.__getattribute__(self, name),而沒(méi)有使用 return self.__dict__[name]。因?yàn)槿绻眠@樣的方式,就是訪問(wèn) self.__dict__,只要訪問(wèn)這個(gè)屬性,就要調(diào)用`getattribute``,這樣就導(dǎo)致了無(wú)限遞歸
# 訪問(wèn)不存在的成員,可以看到,已經(jīng)被__getattribute__攔截了,雖然最后還是要報(bào)錯(cuò)的。
>>
>
b
=
B
(
)
>>
>
b
.
y
you are useing getattribute
Traceback
(
most recent call last
)
:
File
"
"
,
line
1
,
in
<
module
>
File
"
"
,
line
4
,
in
__getattribute__
AttributeError
:
'B'
object
has no attribute
'y'
Property函數(shù)
porperty可以作為裝飾器使用把方法標(biāo)記為特性
class
Vector
(
object
)
:
def
__init__
(
self
,
x
,
y
)
:
# 使用兩個(gè)前導(dǎo)下劃線,把屬性標(biāo)記為私有
self
.
__x
=
float
(
x
)
self
.
__y
=
float
(
y
)
# porperty裝飾器把讀值方法標(biāo)記為特性
@
property
def
x
(
self
)
:
return
self
.
__x
@
property
def
y
(
self
)
:
return
self
.
__y
vector
=
Vector
(
3
,
4
)
print
(
vector
.
x
,
vector
.
y
)
使用property可以將函數(shù)封裝為屬性
class
Rectangle
(
object
)
:
"""
the width and length of Rectangle
"""
def
__init__
(
self
)
:
self
.
width
=
0
self
.
length
=
0
def
setSize
(
self
,
size
)
:
self
.
width
,
self
.
length
=
size
def
getSize
(
self
)
:
return
self
.
width
,
self
.
length
if
__name__
==
"__main__"
:
r
=
Rectangle
(
)
r
.
width
=
3
r
.
length
=
4
print
r
.
getSize
(
)
# (3,4)
r
.
setSize
(
(
30
,
40
)
)
print
r
.
width
# 30
print
r
.
length
# 40
這段代碼可以正常運(yùn)行,但是屬性的調(diào)用方式可以改進(jìn),如下 :
class
Rectangle
(
object
)
:
"""
the width and length of Rectangle
"""
def
__init__
(
self
)
:
self
.
width
=
0
self
.
length
=
0
def
setSize
(
self
,
size
)
:
self
.
width
,
self
.
length
=
size
def
getSize
(
self
)
:
return
self
.
width
,
self
.
length
# 使用property方法將函數(shù)封裝為屬性,更優(yōu)雅
size
=
property
(
getSize
,
setSize
)
if
__name__
==
"__main__"
:
r
=
Rectangle
(
)
r
.
width
=
3
r
.
length
=
4
print
r
.
size
# (30, 40)
r
.
size
=
30
,
40
print
r
.
width
# 30
print
r
.
length
# 40
使用魔術(shù)方法實(shí)現(xiàn):
class
NewRectangle
(
object
)
:
def
__init__
(
self
)
:
self
.
width
=
0
self
.
length
=
0
def
__setattr__
(
self
,
name
,
value
)
:
if
name
==
'size'
:
self
.
width
,
self
,
length
=
value
else
:
self
.
__dict__
[
name
]
=
value
def
__getattr__
(
self
,
name
)
:
if
name
==
'size'
:
return
self
.
width
,
self
.
length
else
:
raise
AttrubuteErrir
if
__name__
==
"__main__"
:
r
=
Rectangle
(
)
r
.
width
=
3
r
.
length
=
4
print
r
.
size
# (30, 40)
r
.
size
=
30
,
40
print
r
.
width
# 30
print
r
.
length
# 40
屬性的獲取順序
最后我們來(lái)看看熟悉的獲得順序:通過(guò)實(shí)例獲取其屬性,如果在__dict__中有相應(yīng)的屬性,就直接返回其結(jié)果;如果沒(méi)有,會(huì)到類屬性中找。
看下面一個(gè)例子:
class
A
(
object
)
:
author
=
"qiwsir"
def
__getattr__
(
self
,
name
)
:
if
name
!=
"author"
:
return
"from starter to master."
if
__name__
==
"__main__"
:
a
=
A
(
)
print
a
.
author
# qiwsir
print
a
.
lang
# from starter to master.
當(dāng) a = A() 后,并沒(méi)有為實(shí)例建立任何屬性,或者說(shuō)實(shí)例的__dict__是空的。但是如果要查看 a.author,因?yàn)閷?shí)例的屬性中沒(méi)有,所以就去類屬性中找,發(fā)現(xiàn)果然有,于是返回其值 “qiwsir”。但是,在找 a.lang的時(shí)候,不僅實(shí)例屬性中沒(méi)有,類屬性中也沒(méi)有,于是就調(diào)用了__getattr__()方法。在上面的類中,有這個(gè)方法,如果沒(méi)有__getattr__()方法呢?如果沒(méi)有定義這個(gè)方法,就會(huì)引發(fā) AttributeError,這在前面已經(jīng)看到了。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
