面向对象进阶当然是要谈谈面向对象的三大特性:封装、继承、多态
@property装饰器
python虽然不建议把属性和方法都设为私有的,但是完全暴露给外界也不好,这样,我们给属性赋值的有效性九无法保证,因此,为了使得对属性的访问既安全又方便,可以通过属性的getter(访问器)和setter(修改器)方法进行对应的操作,在python中,可以考虑使用@property包装器来包装getter和setter方法
class Person(object): def __init__(self,name,age): self._name=name self._age=age #访问器 - getter方法 @property def name(self): return self._name @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("wangbb",16) person.play() #调用setter修改器修改其属性 person.age=21 person.play() #person.name='wbb' #AttributeError: can't set attribute if __name__=='__main__': main()
效果入下图:
__slots__魔法
__slots__魔法是为了让python这个动态语言,限定我们的自定义类型只能绑定特定的属性,而且只对当前类生效,对其子类没有限制作用
class Person(object): # 限定Person类只能绑定_name,_age,_gender属性, # 此处请注意,_name和name不是同一种属性,不能绑定name属性 __slots__ = ('_name','_age','_gender') # Person类初始化 def __init__(self,name,age): self._name=name self._age=age #访问器 - getter方法 @property def name(self): return self._name @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("wangbb",16) person.play() #调用setter修改器修改其属性 person.age=21 person.play() person._gender='男' #person._isgay=True AttributeError: 'Person' object has no attribute '_isgay' if __name__=='__main__': main()
静态方法和类方法
定义在类中的方法有三种:对象方法(前面的大多数都是对象方法)、静态方法、类方法。关于静态方法,举个例子说明:我们定义一个“三角形”类,通过传入三条边长来构造三角形,并提供计算周长和面积的方法,但是传入的三条边长未必能构造出三角形对象,因此我们可以先写一个方法来验证三条边长是否可以构成三角形,这个方法很显然就不是对象方法,因为在调用这个方法时三角形对象尚未创建出来(因为都不知道三条边能不能构成三角形),所以这个方法是属于三角形类而并不属于三角形对象的。我们可以使用静态方法来解决这类问题:
from math import sqrt class Triangle(object): def __init__(self,a,b,c): self._a=a self._b=b self._c=c #静态方法 @staticmethod def is_valid(a,b,c): return a + b > c and a + c > b and b + c > a def perimeter(self): return self._a+self._b+self._c def area(self): href=self.perimeter()/2 return sqrt(href*(href-self._a)*(href-self._b)*(href-self._c)) def main(): a,b,c=3,4,5 #静态方法和类方法都是通过给类发消息来调用的 if Triangle.is_valid(a,b,c): triangle=Triangle(a,b,c) print("三角形的周长是:",triangle.perimeter()) # 也可以通过给类发消息来调用对象方法但是要传入接收消息的对象作为参数 print(Triangle.perimeter(triangle)) print("三角形的面积是:",triangle.area()) print(Triangle.area(triangle)) else: print("不好意思,不能构成三角形!") if __name__=='__main__': main()
和静态方法比较类似,Python还可以在类中定义类方法,类方法的第一个参数约定名为cls,它代表的是当前类相关的信息的对象(类本身也是一个对象,有的地方也称之为类的元数据对象),通过这个参数我们可以获取和类相关的信息并且可以创建出类的对象:
from time import time, localtime, sleep class Clock(object): """数字时钟""" def __init__(self, hour=0, minute=0, second=0): self._hour = hour self._minute = minute self._second = second @classmethod def now(cls): ctime = localtime(time()) return cls(ctime.tm_hour, ctime.tm_min, ctime.tm_sec) def run(self): """走字""" self._second += 1 if self._second == 60: self._second = 0 self._minute += 1 if self._minute == 60: self._minute = 0 self._hour += 1 if self._hour == 24: self._hour = 0 def show(self): """显示时间""" return '%02d:%02d:%02d' % (self._hour, self._minute, self._second) def main(): # 通过类方法创建对象并获取系统时间 clock = Clock.now() while True: print(clock.show()) sleep(1) clock.run() if __name__ == '__main__': main()
类之间的关系
简单的说,类和类之间的关系有三种:is-a、has-a和use-a关系。
- is-a关系也叫继承或泛化,比如学生和人的关系、手机和电子产品的关系都属于继承关系。
- has-a关系通常称之为关联,比如部门和员工的关系,汽车和引擎的关系都属于关联关系;关联关系如果是整体和部分的关联,那么我们称之为聚合关系;如果整体进一步负责了部分的生命周期(整体和部分是不可分割的,同时同在也同时消亡),那么这种就是最强的关联关系,我们称之为合成关系。
- use-a关系通常称之为依赖,比如司机有一个驾驶的行为(方法),其中(的参数)使用到了汽车,那么司机和汽车的关系就是依赖关系。
继承和多态
python可以在已有类的基础上创建新类,这其中的一种做法就是让一个类从另一个类那里将属性和方法直接继承下来,从而减少重复代码的编写。提供继承信息的我们称之为父类,也叫超类或基类;得到继承信息的我们称之为子类,也叫派生类或衍生类。子类除了继承父类提供的属性和方法,还可以定义自己特有的属性和方法,所以子类比父类拥有的更多的能力,子类在继承了父类的方法后,可以对父类已有的方法给出新的实现版本,这个动作称之为方法重写(override)。通过方法重写我们可以让父类的同一个行为在子类中拥有不同的实现版本,当我们调用这个经过子类重写的方法时,不同的子类对象会表现出不同的行为,这个就是多态(poly-morphism)。
Python从语法层面并没有像Java或C#那样提供对抽象类的支持,但是我们可以通过abc
模块的ABCMeta
元类和abstractmethod
包装器来达到抽象类的效果,如果一个类中存在抽象方法那么这个类就不能够实例化(创建对象)。
""" 某公司有三种类型的员工 分别是部门经理、程序员和销售员 需要设计一个工资结算系统 根据提供的员工信息来计算月薪 部门经理的月薪是每月固定15000元 程序员的月薪按本月工作时间计算 每小时150元 销售员的月薪是1200元的底薪加上销售额5%的提成 """ from abc import ABCMeta,abstractmethod class employee(object,metaclass=ABCMeta): def __init__(self,name): self._name=name @property def name(self): return self._name @abstractmethod def get_salary(self): pass class manger(employee): def get_salary(self): return 15000.0 class programer(employee): def __init__(self,name,work_hour=0): super().__init__(name) self._work_hour=work_hour def get_salary(self): return 150.0*self._work_hour @property def work_hour(self): return self._work_hour @work_hour.setter def work_hour(self,work_hour): self._work_hour=work_hour if work_hour>0 else 0 class salesman(employee): def __init__(self,name,sales=0): super().__init__(name) self._sales=sales def get_salary(self): return 1200.0+self._sales*0.05 @property def sales(self): return self._sales @sales.setter def sales(self,sales): self._sales=sales if sales >0 else 0 def main(): emps=[manger('wbb'),programer('bb'),salesman('lj')] for emp in emps: if isinstance(emp,programer): emp.work_hour=int(input("请输入%s本月工作时长:"%emp.name)) elif isinstance(emp,salesman): emp.sales=float(input("请输入%s本月的销售量:"%emp.name)) print("%s本月的工资为:¥%s元"%(emp.name,emp.get_salary())) if __name__=='__main__': main()