Python core notes for test development (22): composition, inheritance and polymorphism

22.1 Multi-purpose combination

Different classes can be mixed and added to other classes to enhance class functionality and code reuse. That is, properties of a class can be instances of other classes. Composition is better when the classes are significantly different and the smaller class is a required component of the larger class.

Let's see an example:

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)

We know that a torus consists of two circles. The area of ​​a ring is the area of ​​the outer circle minus the area of ​​the inner circle, and the circumference of a ring is the outer circumference plus the circumference of the inner circle. Using the idea of ​​class composition, define a torus, and combine the instances of the circle in the torus class as its own attributes:

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())

22.2 Use inheritance sparingly

  • Inheritance means that the subclass has both the characteristics of the parent class (class attributes and functions).
  • Subclasses also have unique characteristics (custom properties and functions) that differ from their parent classes.
  • Python supports multiple inheritance, but try not to.
  • Subclasses can override superclass methods with the same name
  • All classes by default inherit from the object class
  • Check class inheritance with issubclass()
  • __bases__All parent classes of a class can be accessed through properties

22.2.1 Single inheritance

see examples, learn inheritance

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 is the parent class, Document and Video are subclasses that inherit from Entity, and they all inherit the object_type and title properties of the parent class, as well as the get_context_length method. Document also has its own unique attributes author and __context. The get_context_length method of the Document class overrides the method of the parent class.

When a subclass calls a method of the superclass, it can use the super(subclass, self) method. Take a look at the introduction of super in the official website:

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)

As you can see from the definition, it is usually used to call the method of the corresponding parent class. super() is equivalent to super(subclass, self), they both return a superclass object.

It can be judged whether a class is a subclass of another class issubclass.

22.2.2 Multiple Inheritance

Although you can, try not to use it.

22.2.3 The relationship between object and type

In an object-oriented system, there are two relationships:

  • Inheritance relationship, you can pass __bases__attributes to all parent classes of a class (because Python supports multiple inheritance).
  • For the relationship between classes and instances, you can __class__view the type of the instance through properties, or use the type() function to view.

In the Python world, all classes are subclasses of object; all objects are instances of 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'>,)

It can be seen that the object class, type class, list class, tuple class, and dict class are all type types, that is to say, they are instances of the type class. The parent classes of type class, list class, tuple class, and dict class are all object classes, and the object class itself has no parent class.

22.3 Polymorphism is convenient

Or use the above example to learn the application of polymorphism. In main, three classes are instantiated, and a unified interface is defined to access the methods of the instance.

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)

This is polymorphism and refers to the method of using an instance regardless of the instance type. Through this example, you can intuitively feel the advantages of polymorphism:

  • Increased program flexibility

With constant change, no matter the object is ever-changing, the user calls it in the same form, such as get_length(picture)

  • Increased program scalability

A new class Picture is created by inheriting the Entity class. Users do not need to change their own code, or use get_length(picture) to call

class Picture:
    def __init__(self, photographer, pixel):
        self.photographer = photographer
        self.pixel = pixel

    def get_context_length(self):  # 声明同样的方法
        return "像素是 {}".format(self.pixel)

This behavior is called polymorphism. That is, the get_length()method call will act on the actual type of the parameter entity. entity is of type Document, it actually has its own get_context_length()methods and methods inherited from the Entity class get_context_length(), but the call entity.get_context_length()always looks for its own definition first, and if there is no definition, it looks up the inheritance chain until it is in a parent class until found, such as the video in the example above.

Since Python is a dynamic language, get_length()the parameter entity passed to the function is not necessarily a subtype of Entity or Entity. An instance of any data type will do, as long as it has a get_context_length()method.

Guess you like

Origin blog.csdn.net/liuchunming033/article/details/107956591