01 使用__slots__
在上一节内容中,我们创建了Student类。那么,如果在开发中我们想限制实例的属性应该怎么办?比如,只允许Student实例有 name 和 age 属性。
此时,Python的一个特殊变量__slots__就发挥了作用。Python允许在定义class的时候定义一个特殊变量__slots__,从而限制该class实例能添加的属性:
class Student(object):
__slots__ = ('name', 'age') # 用tuple定义允许绑定的属性名称
s = Student() # 创建新的实例
s.name = 'Michael' # 绑定属性'name'
s.age = 25 # 绑定属性'age'
s.score = 99 # 绑定属性'score'
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# AttributeError: 'Student' object has no attribute 'score'
从上面的程序中可以看出,由于score没有被放到__slots__中,所以不能绑定score属性。如果绑定不在__slots__中的属性,将会引起AttributeError的异常。
注意:__slots__的限定只对当前类的对象生效,对子类并不起任何作用。
02 使用@property
在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数,导致用户可以随便更改属性:
s = Student()
s.age = 2000
上面的程序显然不合理,当然我们也可以设置getAge()和setAge()方法。从而限制用户对属性进行不合理的修改。但是这样就会略显麻烦,没有直接修改属性来的简单。
那么如果想访问属性可以通过属性的getter(访问器)和setter(修改器)方法进行对应的操作。如果要做到这点,就可以考虑使用@property包装器来包装getter和setter方法,使得对属性的访问既安全又方便,代码如下所示:
class Person(object):
def __init__(self, name, age):
self._name = name
self._age = age
# 访问器 - getter方法
[@property](https://my.oschina.net/property)
def name(self):
return self._name
# 访问器 - getter方法
[@property](https://my.oschina.net/property)
def age(self):
return self._age
# 修改器 - setter方法
@age.setter
def age(self, age):
self._age = age
def play(self):
if self._age <= 16:
print('%s正在玩飞行棋.' % self._name)
else:
print('%s正在玩斗地主.' % self._name)
def main():
person = Person('王大锤', 12)
person.play()
person.age = 22
person.play()
# person.name = '白元芳' # AttributeError: can't set attribute
if __name__ == '__main__':
main()
03 类之间的关系
简单的说,类和类之间的关系有三种:is-a、has-a和use-a关系。
is-a关系也叫继承或泛化,比如学生和人的关系、手机和电子产品的关系都属于继承关系。
has-a关系通常称之为关联,比如部门和员工的关系,汽车和引擎的关系都属于关联关系;关联关系如果是整体和部分的关联,那么我们称之为聚合关系;如果整体进一步负责了部分的生命周期(整体和部分是不可分割的,同时同在也同时消亡),那么这种就是最强的关联关系,我们称之为合成关系。
use-a关系通常称之为依赖,比如司机有一个驾驶的行为(方法),其中(的参数)使用到了汽车,那么司机和汽车的关系就是依赖关系。
04 继承和多态
在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)。
假设我们定义一个Animal的类,里面包含一个run方法:
class Animal(object):
def run(self):
print('Animal is running...')
那么当我们想要写Cat和Dog类的时候,就可以直接从Animal类继承:
class Dog(Animal):
pass
class Cat(Animal):
pass
对于Dog类来说,Animal就是它的父类,对于Animal来说,Dog就是它的子类。Cat和Dog类似。
继承有什么好处?最大的好处是子类获得了父类的全部功能。由于Animial实现了run()方法,因此,Dog和Cat作为它的子类,什么事也没干,就自动拥有了run()方法。
子类在继承了父类的方法后,可以对父类已有的方法给出新的实现版本,这个动作称之为方法重写(override)。
通过方法重写我们可以让父类的同一个行为在子类中拥有不同的实现版本,当我们调用这个经过子类重写的方法时,不同的子类对象会表现出不同的行为,这个就是多态(poly-morphism)。
05 每日小测
某公司有三种类型的员工 分别是部门经理、程序员和销售员
需要设计一个工资结算系统 根据提供的员工信息来计算月薪
部门经理的月薪是每月固定15000元
程序员的月薪按本月工作时间计算 每小时150元
销售员的月薪是1200元的底薪加上销售额5%的提成