本文最后更新于:14 天前

多继承和魔法方法

在上一节中,我们讲了类的定义,属性和方法,那么我们这个节课来看看多继承魔法方法

多继承

在上节我们讲到了继承,一个类可以继承一个类,继承之后可以把父类所有的方法和属性都直接继承过来,那一个类可以继承多个类呢?

如果可以继承多个类的话,那如果两个父类中有一样的方法的情况下,子类继承哪一个呢?

演示

class Base:
    def play(self):
        return 'this is Base'


class A(Base):
    def play(self):
        return 'this is A'


class B(Base):
    def play(self):
        return 'this is B'
 

class C(A,B):
    pass

c = C()
print(c.play())

输出

this is A

首先类是可以多继承的

优先使用第一个类里面的方法。

总结

1

通过C类实例的方法调用来看

当继承多个父类时,如果父类中有相同的方法,那么子类会优先使用最先被继承的方法.

重构

在上面的例子中,如果不想继承父类的方法怎么办呢?

class Base:
    def play(self):
        return 'this is Base'


class A(Base):
    def play(self):
        return 'this is A'


class B(Base):
    def play(self):
        return 'this is B'


class C(A, B):
    def play(self):
        return 'this is C'


c = C()
print(c.play())

输出

this is C

当子类继承父类之后,如果子类不想使用父类的方法,可以通过重写来覆盖父类的方法.

扩展

重写父类方法之后,如果又需要使用父类的方法呢?

方法一
class Base:
    def play(self):
        print('this is Base')


class A(Base):
    def play(self):
        print('this is A')


class B(Base):
    def play(self):
        print('this is B')


class C(A, B):

    def play(self):
        A.play(self)
        print('这是C')


demo = C()
demo.play()

输出

this is A
这是C
方法二
class Base:
    def play(self):
        print('this is Base')


class A(Base):
    def play(self):
        print('this is A')


class B(Base):
    def play(self):
        print('this is B')


class C(A, B):

    def play(self):
        super().play()
        print('这是C')


demo = C()
demo.play()

输出

this is A
这是C

super

super函数可以调用父类的方法

class Base:
    def play(self):
        print('this is Base')


class A(Base):
    def play(self):
        super().play()
        print('this is A')


class B(Base):
    def play(self):
        super().play()
        print('this is B')


class C(A, B):

    def play(self):
        super().play()
        print('这是C')


demo = C()
demo.play()

输出

this is Base
this is B
this is A
这是C

那为什么是这个顺序输出呢?

print(C.mro())

输出

[<class '__main__.C'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.Base'>, <class 'object'>]

继承顺序:

2

在python3中,类被创建时会自动创建方法解析顺序mro

object是所有类的父类

Mixin开发模式

3

Mixin是一种开发模式,一般规范上,Mixin类是继承的终点,即不再被继承

Mixin的优点就是不需要过多考虑继承关系,不会出现各父类之间有相同方法的情况

总结

mro: 类在生成时会自动生成方法解析顺序,可以通过 类名.mro()来查看

super: super函数可以来调用父类的方法,使用super的好处在于即使父类改变了,那么也不需要更改类中的代码

Mixin: Mixin是一种开发模式,给大家在今后的开发中提供一种思路.

魔法方法

在讲字符串拼接的时候,字符串可以直接相加,那我们自定义的类可以实现吗?

class Rectangle:

    # 传入长和宽
    def __init__(self, length, width):
        self.length = length
        self.width = width

    def area(self):
        area = self.width * self.length
        return area

    def __add__(self, other):
        add_length = self.length + other.length
        add_width = self.width + other.width
        return add_length,add_width

a = Rectangle(3,4)
b = Rectangle(5,6)
print(a+b)

输出

(8, 10)

运算方法

4

运算方法大家了解下就行,在实际运用中用不并不多。

add和radd原理
class A:
    pass


class B:


    def __add__(self, other):
        print('__add__')

    def __radd__(self, other):
        print('__radd__')

a = A()
b = B()
a+b

输出

__radd__

优先在两类里找add方法,没有就自动调用radd方法。

演示

class A:
    def __init__(self,name,age):
        self.name =  name
        self.age = age


class B:

    def __init__(self,age):
        self.age = age

    def __add__(self, other):
        print('__add__')

    def __radd__(self, other):
        return other.age + self.age

a = A('age',123)
b = B(123)
print(a+b)

输出

246

str和repr原理

class Rectangle:

    # 传入长和宽
    def __init__(self, length, width):
        self.length = length
        self.width = width

    def area(self):
        area = self.width * self.length
        return area

    def __add__(self, other):
        add_length = self.length + other.length
        add_width = self.width + other.width
        return add_length, add_width

    def __radd__(self, other):
        return "Rectangle radd"

    def __str__(self):
        return 'length is %s, width is %s ' % (self.length, self.width)

    def __repr__(self):
        return 'area  is %s' % self.area()


a = Rectangle(3,4)
b = Rectangle(5,6)
print(a+b)
print(a.__add__(b))

print(a)

输出

# 有str
(8, 10)
(8, 10)
length is 3, width is 4 
# 无str
(8, 10)
(8, 10)
area  is 12

优先在两类里找str方法,没有就自动调用repr方法。

在python中,str和repr方法在处理对象的时候,分别调用的是对象的strrepr方法

print也是如此,调用str函数来处理输出的对象,如果对象没有定义str方法,则调用repr处理

call方法

class Rectangle:

    # 传入长和宽
    def __init__(self, length, width):
        self.length = length
        self.width = width

    def area(self):
        area = self.width * self.length
        return area

    def __add__(self, other):
        add_length = self.length + other.length
        add_width = self.width + other.width
        return add_length, add_width

    def __radd__(self, other):
        return "Rectangle radd"

    def __str__(self):
        return 'length is %s, width is %s ' % (self.length, self.width)

    def __repr__(self):
        return 'area  is %s' % self.area()

    def __call__(self):
        return 'Rectangle called'


a = Rectangle(3,4)
b = Rectangle(5,6)
print(a())

输出

Rectangle called

正常情况下,实例是不能像函数一样被调用的,要想实例能够被调用,就需要定义 call 方法

其他魔法方法

5

演示

class Rectangle:

    # 传入长和宽
    def __init__(self, length, width):
        self.length = length
        self.width = width

    def area(self):
        area = self.width * self.length
        return area

    def __add__(self, other):
        add_length = self.length + other.length
        add_width = self.width + other.width
        return add_length, add_width

    def __radd__(self, other):
        return "Rectangle radd"

    def __str__(self):
        return 'length is %s, width is %s ' % (self.length, self.width)

    def __repr__(self):
        return 'area  is %s' % self.area()

    def __call__(self):
        return 'Rectangle called'


a = Rectangle(3,4)

print(a.__class__)
print(a.__class__.__base__)
print(a.__class__.__bases__)
print(a.__dict__)   # 所有属性,键值对返回
print(a.__doc__)
print(a.__dir__())

输出

<class '__main__.Rectangle'>
<class 'object'>
(<class 'object'>,)
{'length': 3, 'width': 4}
None
['length', 'width', '__module__', '__init__', 'area', '__add__', '__radd__', '__str__', '__repr__', '__call__', '__dict__', '__weakref__', '__doc__', '__hash__', '__getattribute__', '__setattr__', '__delattr__', '__lt__', '__le__', '__eq__', '__ne__', '__gt__', '__ge__', '__new__', '__reduce_ex__', '__reduce__', '__subclasshook__', '__init_subclass__', '__format__', '__sizeof__', '__dir__', '__class__']

简单了解。

魔法方法应用场景

strrepr: str和repr都是分别调用这两个魔术方法来实现的

原理:在类中,很多事情其实调用的魔术方法来实现的

作用:通过合理的利用魔术方法,可以让我们更加方便的展示我们的数据