__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