__slots__
限制class實例能添加的屬性
class 類名(object):
__slots__ = (‘a', ‘b') # 用tuple定義允許綁定的屬性名稱```
`__slots__`定義的屬性僅對當前類實例起作用,對繼承的子類是不起作用的。除非在子類中也定義`__slots__`,這樣,**子類實例允許定義的屬性就是自身的`__slots__`加上父類的`__slots__`**。
**多重繼承**
只需要在`class a(多寫幾個就可以啦,都需要是已經定義過的類)`
`MixIn`只是為了便于區分自行在類名后面加上的,不是一種方法
**重要?。。∈褂聾property**
`@property `將方法變成了屬性,就跟寫在__init__中一樣。因此其不能用()調用,只能直接賦值
這時候你可以決定這個屬性可以讀寫或者只讀。
只讀: 不加setter方法。
讀寫:再加一個`@a.setter`方法
另外:沒有`__init__`就沒有屬性,除非在方法中輸入數據。
a = 1
b = a
a = 2
b = 1
b指向的不是a而是1這個數據的內存位置,每次給變量賦值都是直接到位置,而不是指向變量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```
不知道哪里錯了就對比了教程前面的代碼,發現是少了下劃線,于是加上測試。
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方法已經變成了屬性,調用的時候直接self.width,所以內部的數據要用self._width更合適,否則會重名造成嵌套死循環
循環次數過多的時候就會造成錯誤:
RecursionError: maximum recursion depth exceeded
最終結果
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輸出就一樣了,一個是方法變成的屬性,一個是方法中返回的屬性。
然后發現自己還沒有給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???為什么沒報錯?
然后把教程里的代碼試了一下 發現依然不報錯..不懂。
------
解決:怪我前面不認真看,結合前面的限制訪問章節。
1.其實@property和@a.setter實際上是看做是定義的一個函數,其中的`s._width`可以省略,相當于直接return value,于是如果是使用`s.width`,就相當于是修改value,如果value滿足了條件就會提示錯誤。
2.但是如果是使用`s._width`,其實只是一個傳遞的媒介,改變了self.width的屬性值,但這時候的屬性值并不是通過value傳入的,而是從中間傳入,因此不會出錯,因為條件判斷的是value的值。
3.其實實際調用方法查看屬性的時候,我們是不知道內部函數的,如果你不小心知道了,就可以隨便修改,就很不安全。雖然我們默認一個下劃線就屬于private內部變量,但是全靠自覺。但是不是每個人都是好人的?。?So!如果想要隔絕內部與外部的聯系,看限制訪問章節,提供了一個方法:
把內部的變量定義為`s.__width`(2個下劃線),來限制訪問,注意區分,如果是`__init__`之類的前后都有兩個下劃線的是外部變量。即你在外部如果修改`s.__width`是做不到的,這時候寫`s.__width=...`相當于重新加了一個外部變量(屬性),即內部和外部的變量名相同但其實不一樣。這時候就算你知道了內部函數名也修改不了它的值。它已經被解析為了`_類名__name`
**這就告訴我們,如果是自己寫程序的話,在定義方法的時候,一定要注意限制訪問**
我把定義中的`_height`改為`__height`有下面的結果:
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