在了解了面向对象基础之后,我们知道了如何定义类,如何创建对象以及给对象发消息。为了更好使用面向对象思想进行编程开发,需要对python中面向对象编程有更深入的了解。
1.@property装饰器
2.__slots__魔法
3.静态方法和类方法
4.类之间的关系
5.继承和多态
6.综合案例演示
@property装饰器
之前讨论过python中属性和方法有权限的问题,虽然不建议将属性设为私有,但是直接把属性暴露给外界是有问题的,比如 没有办法检查赋给属性的值是否有效。之间的措施是将属性以单下滑线开头,通过这种方法暗示属性受保护,不建议外界直接访问,那么如果想访问属性就可以通过属性的getter(访问器)和setter(控制器)方法来进行对应的操作。如果要实现这点,就可以考虑使用@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
#访问器getter方法
@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('zhangsan',10)
person.play()
person.age = 22
person.play()
if __name__=="__main__":
main()
__slots__方法
python作为一门动态语言。通常动态语言允许我们在程序运行时给对象绑定新的属性或方法,当然也可以对已绑定的属性和方法进行解绑。如果我们要限定自定义类型的对象只能绑定某些属性,就可以通过在类中定义__slots__变量来进行限定。需要注意__slots__的限定只对当前类的对象生效,对子类不起作用。
class Person(object):
__slots__=('_name','_age','_gender')
def __init__(self,name,age):
self._name = name
self._age = age
@property
def name(self):
return self._name
@property
def age(self):
return self._age
@age.setter
def age(self,age):
self._age =age
def play(self):
print('%sZZZZZZZZZZZZZZZZ'%self._name)
def main():
person = Person('zhangsan',21)
person.play()
person._gender='man'
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):
half = self.perimeter()/2
return sqrt(half*(half - self._a)*(half - self._b)*(half - self._c))
def main():
a,b,c = 3,4,5
if Triangle.is_valid(a,b,c):
t = Triangle(a,b,c)
print(t.perimeter())
print(t.area())
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,比如司机有一个驾驶的行为(方法),其中用到了汽车,那么司机和汽车就构成了依赖的关系。
继承和多态
刚才我们提到了,可以在已有类的基础上创建新类,这其中的一种做法就是让一个类从另一个类那里将属性和方法直接继承下来,从而减少重复代码的编写。提供继承信息的我们称之为父类,也叫超类或基类;得到继承信息的我们称之为子类,也叫派生类或衍生类。子类除了继承父类提供的属性和方法,还可以定义自己特有的属性和方法,所以子类比父类拥有的更多的能力,在实际开发中,我们经常会用子类对象去替换掉一个父类对象,这是面向对象编程中一个常见的行为。
class Person(object):
"""人"""
def __init__(self, name, age):
self._name = name
self._age = age
@property
def name(self):
return self._name
@property
def age(self):
return self._age
@age.setter
def age(self, age):
self._age = age
def play(self):
print('%s正在愉快的玩耍.' % self._name)
def watch_av(self):
if self._age >= 18:
print('%s正在观看爱情动作片.' % self._name)
else:
print('%s只能观看《熊出没》.' % self._name)
class Student(Person):
"""学生"""
def __init__(self, name, age, grade):
super().__init__(name, age)
self._grade = grade
@property
def grade(self):
return self._grade
@grade.setter
def grade(self, grade):
self._grade = grade
def study(self, course):
print('%s的%s正在学习%s.' % (self._grade, self._name, course))
class Teacher(Person):
"""老师"""
def __init__(self, name, age, title):
super().__init__(name, age)
self._title = title
@property
def title(self):
return self._title
@title.setter
def title(self, title):
self._title = title
def teach(self, course):
print('%s%s正在讲%s.' % (self._name, self._title, course))
def main():
stu = Student('王大锤', 15, '初三')
stu.study('数学')
stu.watch_av()
t = Teacher('骆昊', 38, '砖家')
t.teach('Python程序设计')
t.watch_av()
if __name__ == '__main__':
main()
子类在继承了父类的方法后,可以对父类已有的方法给出新的实现版本,这个动作称之为方法重写(override)。通过方法重写我们可以让父类的同一个行为在子类中拥有不同的实现版本,当我们调用这个经过子类重写的方法时,不同的子类对象会表现出不同的行为,这个就是多态(poly-morphism)。
from abc import ABCMeta, abstractmethod
class Pet(object, metaclass=ABCMeta):
"""宠物"""
def __init__(self, nickname):
self._nickname = nickname
@abstractmethod
def make_voice(self):
"""发出声音"""
pass
class Dog(Pet):
"""狗"""
def make_voice(self):
print('%s: 汪汪汪...' % self._nickname)
class Cat(Pet):
"""猫"""
def make_voice(self):
print('%s: 喵...喵...' % self._nickname)
def main():
pets = [Dog('旺财'), Cat('凯蒂'), Dog('大黄')]
for pet in pets:
pet.make_voice()
if __name__ == '__main__':
main()
在上面的代码中,我们将Pet类处理成了一个抽象类,所谓抽象类就是不能够创建对象的类,这种类的存在就是专门为了让其他类去继承它。Python从语法层面并没有像Java或C#那样提供对抽象类的支持,但是我们可以通过abc模块的ABCMeta元类和abstractmethod包装器来达到抽象类的效果,如果一个类中存在抽象方法那么这个类就不能够实例化(创建对象)。上面的代码中,Dog和Cat两个子类分别对Pet类中的make_voice抽象方法进行了重写并给出了不同的实现版本,当我们在main函数中调用该方法时,这个方法就表现出了多态行为(同样的方法做了不同的事情)。