1、多用组合
不同的类可以混合使用,加入到其他类中,来增强类的功能和代码重用性。也就是一个类的属性可以是其他类的实例。当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好。
来看一个例子:
class Name: def __init__(self, first_name, last_name): self.first_name = first_name self.last_name = last_name def __str__(self): return "{}.{}".format(self.first_name, self.last_name) class Address: def __init__(self, province, city): self.province = province self.city = city def __str__(self): return "{}-{}".format(self.province, self.city) class Person: def __init__(self, name, address): self.name = name self.address = address def __str__(self): return "姓名:{},地址:{}".format(self.name, self.address) if __name__ == '__main__': p = Person(Name("Chunming", "liu"), Address("beijing", "beijing")) print(p)
我们知道圆环是由两个圆组成的。圆环的面积是外圆的面积减去内圆的面积,圆环的周长是外圆周长加上内圆的周长。用类的组成思路,定义圆环,在圆环类中组合圆形的实例作为自己的属性:
from math import pi class Circle: def __init__(self, radius): self.radius = radius def area(self): return pi * self.radius * self.radius def perimeter(self): return 2 * pi * self.radius class Ring: def __init__(self, inner_radius, outer_radius): self.inner_radius = Circle(inner_radius) # 组合圆形类作为自己的属性 self.outer_radius = Circle(outer_radius) def area(self): return self.outer_radius.area() - self.inner_radius.area() # 重用圆形类的方法 def perimeter(self): return self.inner_radius.perimeter() + self.outer_radius.perimeter() if __name__ == '__main__': r = Ring(10, 20) print(r.area()) print(r.perimeter())
2、少用继承
继承,指的是子类既拥有父类的特征(类的属性和函数)。
子类也拥有不同于父类的独特特征(自定义的属性和函数)。
Python支持多继承,但是尽量别这么做。
子类可以覆盖父类的同名方法
所有的类默认情况下都是继承自object类
用issubclass() 检查类继承关系
通过__bases__属性可以类的所有父类
2.1单继承
看例子,学继承
class Entity(object): # 父类 def __init__(self, object_type, title): print('parent class init called') self.object_type = object_type self.title = title def get_context_length(self): return None def __repr__(self): return self.title + "." + self.object_type class Document(Entity): # 1.括号中写父类,可以写多个,表示继承多个类 def __init__(self, object_type, title, author, context): print('Document class init called') super(Document, self).__init__(object_type, title) # 2.自定义了__init__,则必须显示调用父类构造方法 self.author = author # 3.新增自己的属性 self.__context = context def get_context_length(self): # 4.覆盖父类的方法 return len(self.__context) class Video(Entity): pass # 没有复写__init__方法,则会自动调用父类的__init__方法完成初始化。 class Music(Entity): def __init__(self, object_type, title, singer, category): print('Document class init called') super().__init__(object_type, title) self.singer = singer self.category = category def get_context_length(self): return "此音乐时长是4分18秒" if __name__ == '__main__': doc = Document("docx", "Python教程", "chunming", "写给测试人员的Python教程") print(doc) print(doc.get_context_length()) video = Video('mp4', "Python视频教程") print(video) print(issubclass(Video, Entity)) print(video.get_context_length())
Entity是父类,Document和Video是继承自Entity的子类,他们都继承了父类的object_type和title属性,以及get_context_length方法。Document还有自己的独特属性author和__context。Document类的get_context_length方法覆盖了父类的方法。
当子类调用父类的方法时,可以通过super(子类,self)方式。看看官网中super的介绍:
class super(object) | super() -> same as super(class, ) | super(type) -> unbound super object | super(type, obj) -> bound super object; requires isinstance(obj, type) | super(type, type2) -> bound super object; requires issubclass(type2, type) | Typical use to call a cooperative superclass method: | class C(B): | def meth(self, arg): | super().meth(arg) | This works for class methods too: | class C(B): | @classmethod | def cmeth(cls, arg): | super().cmeth(arg)
从定义中可以看到,通常用来调用对应的父类的方法。super() 与 super(子类,self)的方式是等价的,他们都会返回一个父类对象。
判断一个类是不是另外一个类的子类,可以通过issubclass判断。
2.2 多继承
虽然可以,但是尽量不用。
2.3 object和type的关系
在面向对象体系里面,存在两种关系:
继承关系,可以通过__bases__属性可以类的所有父类(因为Python支持多继承)。
类与实例关系,可以通过__class__属性查看实例的类型,或者使用type()函数查看。
在Python的世界中,所有类都是object的子类;所有对象都是type的实例。
class Parent: pass class Child(Parent): pass if __name__ == '__main__': p = Parent() c = Child() print(Parent.__bases__) # 获取Parent类的父类,输出为(<class 'object'>,) print(Child.__bases__) # 获取Child类的父类,输出(<class '__main__.Parent'>,) print(c.__class__) # 获取c的类型,输出<class '__main__.Child'> print(type(c)) # 等同于c.__class__ print(object.__class__) # object类是type类型,输出<class 'type'> print(object.__bases__) # object类 无父类,输出为() print(type.__class__) # type类是type类型,输出<class 'type'> print(type.__bases__) # type类 的父类是object类,输出为(<class 'object'>,) print(list.__class__) # list类是type类型,输出<class 'type'> print(list.__bases__) # list类的父类是object类,输出(<class 'object'>,) print(tuple.__class__) # tuple类是type类型,输出<class 'type'> print(tuple.__bases__) # tuple类的父类是object类,输出(<class 'object'>,) print(dict.__class__) # dict类是type类型,输出<class 'type'> print(dict.__bases__) # dict类的父类是object类,输出(<class 'object'>,)
可见object类、type类、list类、tuple类、dict类都是type类型,也就是说是type类的实例。type类、list类、tuple类、dict类的父类都是object类,object类本身没有父类。
3、多态很方便
还是用上面的例子,学多态的应用。在main里面,实例化三个类,并且定义了一个统一的接口来访问实例的方法。
if __name__ == '__main__': doc = Document("docx", "Python教程", "chunming", "写个测试人员的Python教程") music = Music('mp4', "风吹麦浪", "李键", "抒情") video = Video('mp4', "Python视频教程") # print(doc.get_context_length()) # print(music.get_context_length()) def get_length(entity): # 统一接口,根据传进来的实例类型自动找到它的方法执行 print(entity.get_context_length()) # 访问实例的方法。 get_length(doc) get_length(music) get_length(video)
这就是多态,指在不考虑实例类型的情况下使用实例的方法。通过这个例子,能够直观感受到多态的优点:
增加了程序的灵活性
以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如get_length(picture)
增加了程序额可扩展性
通过继承Entity类创建了一个新的类Picture,使用者无需更改自己的代码,还是用get_length(picture)去调用
class Picture: def __init__(self, photographer, pixel): self.photographer = photographer self.pixel = pixel def get_context_length(self): # 声明同样的方法 return "像素是 {}".format(self.pixel)
这种行为称为多态。也就是说,get_length()方法调用将作用在 参数entity的实际类型上。entity是Document类型,它实际上拥有自己的 get_context_length()方法以及从 Entity类继承的 get_context_length()方法,但调用 entity.get_context_length()总是先查找它自身的定义,如果没有定义,则顺着继承链向上查找,直到在某个父类中找到为止,例如上面例子中的video。
由于Python是动态语言,所以,传递给函数get_length()的参数 entity 不一定是 Entity 或 Entity 的子类型。任何数据类型的实例都可以,只要它有一个get_context_length()的方法即可。
作者:liuchunming033
原文链接:https://blog.csdn.net/liuchunming033/article/details/107956591#comments_13222935