__slots__
限制class實例能添加的屬性
class 類名(object):
__slots__ = (‘a(chǎn)', ‘b') # 用tuple定義允許綁定的屬性名稱```
`__slots__`定義的屬性僅對當前類實例起作用跌榔,對繼承的子類是不起作用的痹愚。除非在子類中也定義`__slots__`序苏,這樣,**子類實例允許定義的屬性就是自身的`__slots__`加上父類的`__slots__`**。
**多重繼承**
只需要在`class a(多寫幾個就可以啦,都需要是已經(jīng)定義過的類)`
`MixIn`只是為了便于區(qū)分自行在類名后面加上的,不是一種方法
**重要!F悄弧!使用@property**
`@property `將方法變成了屬性颖杏,就跟寫在__init__中一樣纯陨。因此其不能用()調(diào)用,只能直接賦值
這時候你可以決定這個屬性可以讀寫或者只讀留储。
只讀: 不加setter方法翼抠。
讀寫:再加一個`@a.setter`方法
另外:沒有`__init__`就沒有屬性,除非在方法中輸入數(shù)據(jù)获讳。
a = 1
b = a
a = 2
b = 1
b指向的不是a而是1這個數(shù)據(jù)的內(nèi)存位置阴颖,每次給變量賦值都是直接到位置,而不是指向變量a丐膝,實例與類也是如此量愧。
定義a為類1
定義實例在類1中
重新定義a為類2
實例仍然在類1中 ```
練習
>>> class Screen(object):
... @property
... def width(self):
... return self.width
... @width.setter
... def width(self,value):
... self.width = value
...
>>> s = Screen()
>>> s.width = 1024
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 7, in width
File "<stdin>", line 7, in width
File "<stdin>", line 7, in width
[Previous line repeated 495 more times]
RecursionError: maximum recursion depth exceeded```
不知道哪里錯了就對比了教程前面的代碼,發(fā)現(xiàn)是少了下劃線帅矗,于是加上測試偎肃。
class Screen(object):
... @property
... def width(self):
... return self._width
... @width.setter
... def width(self,value):
... self._width = value
...
s = Screen()
s._width = 1024
然后就沒有報錯,WHY浑此!
原因
因為此時的width方法已經(jīng)變成了屬性累颂,調(diào)用的時候直接self.width,所以內(nèi)部的數(shù)據(jù)要用self._width更合適凛俱,否則會重名造成嵌套死循環(huán)
循環(huán)次數(shù)過多的時候就會造成錯誤:
RecursionError: maximum recursion depth exceeded
最終結(jié)果
class Screen(object):
@property
def width(self):
return self._width
@width.setter
def width(self,value):
self._width = value
@property
def height(self):
return self._height
@height.setter
def height(self,value2):
self._height = value2
@property
def resolution(self):
return self._height * self._width
s = Screen()
s._width = 1024
s.height = 768
print(s.resolution)```
輸出正確啦紊馏!這時候的s.height 其實和s._height輸出就一樣了,一個是方法變成的屬性蒲犬,一個是方法中返回的屬性朱监。
然后發(fā)現(xiàn)自己還沒有給setter加上限制條件。
class Screen(object):
@property
def width(self):
return self._width
@width.setter
def width(self,value):
if not isinstance(value,int):
raise ValueError('width must be an integer!')
if value < 0:
raise ValueError('width must bigger then 0 ')
self._width = value
@property
def height(self):
return self._height
@height.setter
def height(self,value2):
if not isinstance(value2,int):
raise ValueError('height must be an integer!')
if value2 < 0:
raise ValueError('height must bigger then 0')
self._height = value2
@property
def resolution(self):
return self._height * self._width```
然而 暖哨,可是
>>> s = Screen()
>>> s._width = '1'
>>> s.height = 10
>>> s.resolution
'1111111111'```
WHAT赌朋?凰狞?篇裁?為什么沒報錯?
然后把教程里的代碼試了一下 發(fā)現(xiàn)依然不報錯..不懂赡若。
------
解決:怪我前面不認真看达布,結(jié)合前面的限制訪問章節(jié)。
1.其實@property和@a.setter實際上是看做是定義的一個函數(shù)逾冬,其中的`s._width`可以省略黍聂,相當于直接return value躺苦,于是如果是使用`s.width`,就相當于是修改value产还,如果value滿足了條件就會提示錯誤匹厘。
2.但是如果是使用`s._width`,其實只是一個傳遞的媒介脐区,改變了self.width的屬性值愈诚,但這時候的屬性值并不是通過value傳入的,而是從中間傳入牛隅,因此不會出錯炕柔,因為條件判斷的是value的值。
3.其實實際調(diào)用方法查看屬性的時候媒佣,我們是不知道內(nèi)部函數(shù)的匕累,如果你不小心知道了,就可以隨便修改默伍,就很不安全欢嘿。雖然我們默認一個下劃線就屬于private內(nèi)部變量,但是全靠自覺巡验。但是不是每個人都是好人的<什濉!
So显设!如果想要隔絕內(nèi)部與外部的聯(lián)系框弛,看限制訪問章節(jié),提供了一個方法:
把內(nèi)部的變量定義為`s.__width`(2個下劃線)捕捂,來限制訪問瑟枫,注意區(qū)分,如果是`__init__`之類的前后都有兩個下劃線的是外部變量指攒。即你在外部如果修改`s.__width`是做不到的慷妙,這時候?qū)慲s.__width=...`相當于重新加了一個外部變量(屬性),即內(nèi)部和外部的變量名相同但其實不一樣允悦。這時候就算你知道了內(nèi)部函數(shù)名也修改不了它的值膝擂。它已經(jīng)被解析為了`_類名__name`
**這就告訴我們,如果是自己寫程序的話隙弛,在定義方法的時候架馋,一定要注意限制訪問**
我把定義中的`_height`改為`__height`有下面的結(jié)果:
a = Screen()
a.height
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 14, in height
AttributeError: 'Screen' object has no attribute '_Screen__height'
a.height = '11'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 18, in height
ValueError: height must be an integer!
a.__height = 123
a.__height
123
a.height = 12
a.__height
123
a.height
12