python面向对象进阶

时间:2022-07-05 03:33:20

1.使用  @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('王大锤', 12) person.play() person.age = 22 person.play() # person.name = '白元芳' # AttributeError: can't set attribute if __name__ == '__main__': main()

2.通过在类中定义__slots__变量来限定自定义类型的对象只能绑定某些属性。(只对当前类的对象生效,对子类不影响)
例子:
lass Person(object): # 限定Person对象只能绑定_name, _age和_gender属性 __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): if self._age <= 16: print('%s正在玩飞行棋.' % self._name) else: print('%s正在玩斗地主.' % self._name) def main(): person = Person('王大锤', 22) person.play() person._gender = '男' # AttributeError: 'Person' object has no attribute '_is_gay' # person._is_gay = True

3.静态方法和类方法
之前,我们在类中定义的方法都是对象方法,也就是说这些方法都是发送给对象的消息。实际上,我们写在类中的方法并不需要都是对象方法,
例如我们定义一个“三角形”类,通过传入三条边长来构造三角形,并提供计算周长和面积的方法,但是传入的三条边长未必能构造出三角形对象,
因此我们可以先写一个方法来验证三条边长是否可以构成三角形,这个方法很显然就不是对象方法,因为在调用这个方法时三角形对象尚未创建出来(因为都不知道三条边能不能构成三角形),
所以这个方法是属于三角形类而并不属于三角形对象的。我们可以使用静态方法来解决这类问题,代码如下所示。
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 b + c > a and a + c > b 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,5,7
  a = int(input('请输入第一条边:'))
  b = int(input('请输入第二条边:'))
  c = int(input('请输入第三条边:'))

# 静态方法和类方法都是通过给类发消息来调用的
 if Triangle.is_valid(a, b, c): t = Triangle(a, b, c) print(t.perimeter()) # 也可以通过给类发消息来调用对象方法但是要传入接收消息的对象作为参数 # print(Triangle.perimeter(t)) print(t.area()) # print(Triangle.area(t)) else: print('无法构成三角形.') if __name__ == '__main__': main()


和静态方法比较类似,Python还可以在类中定义类方法,类方法的第一个参数约定名为cls,它代表的是当前类相关的信息的对象(类本身也是一个对象,有的地方也称之为类的元数据对象),
通过这个参数我们可以获取和类相关的信息并且可以创建出类的对象,代码如下所示。
from time import time, localtime, slee
 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()

4.
类之间的关系

简单的说,类和类之间的关系有三种:is-a、has-a和use-a关系。

  • is-a关系也叫继承或泛化,比如学生和人的关系、手机和电子产品的关系都属于继承关系。
  • has-a关系通常称之为关联,比如部门和员工的关系,汽车和引擎的关系都属于关联关系;关联关系如果是整体和部分的关联,那么我们称之为聚合关系;如果整体进一步负责了部分的生命周期(整体和部分是不可分割的,同时同在也同时消亡),那么这种就是最强的关联关系,我们称之为合成关系。
  • use-a关系通常称之为依赖,比如司机有一个驾驶的行为(方法),其中(的参数)使用到了汽车,那么司机和汽车的关系就是依赖关系。
 
5.继承和多态
提供继承信息的我们称之为父类,也叫超类或基类;得到继承信息的我们称之为子类,也叫派生类或衍生类。
子类除了继承父类提供的属性和方法,还可以定义自己特有的属性和方法,所以子类比父类拥有的更多的能力,
在实际开发中,我们经常会用子类对象去替换掉一个父类对象,这是面向对象编程中一个常见的行为,对应的原则称之为里氏替换原则
例子:

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 %d正在看岛国爱情动作片。'%(self._name,self.age))
else:
print('%s 还小,只能看《熊出没》。'%self._name)

class Student(Person):
def __init__(self,name,age,grade):
self._name = name
self._age = 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 Teach(Person):
def __init__(self,name,age,title):
self._name = name
self._age = 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():
person = Person('小王',18)
person.play()
person.watch_av()
print('*****')
stu = Student('老王',22,'研究生')
stu.study('python')
stu.play()
stu.watch_av()
print('*****')
t = Teach('老王',22,'帅得一匹')
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():
pet = Dog('旺财')
pet.make_voice()
pets = [Dog('peter'),Cat('kity'),Dog('tom')]
for pet in pets:
pet.make_voice()


if __name__ == '__main__':
main()

 

在上面的代码中,我们将Pet类处理成了一个抽象类,所谓抽象类就是不能够创建对象的类,这种类的存在就是专门为了让其他类去继承它。Python从语法层面并没有像Java或C#那样提供对抽象类的支持,但是我们可以通过abc模块的ABCMeta元类和abstractmethod包装器来达到抽象类的效果,如果一个类中存在抽象方法那么这个类就不能够实例化(创建对象)。上面的代码中,DogCat两个子类分别对Pet类中的make_voice抽象方法进行了重写并给出了不同的实现版本,当我们在main函数中调用该方法时,这个方法就表现出了多态行为(同样的方法做了不同的事情)。