曲曲的秘密学术基地

纯化欲望、坚持严肃性

欢迎!我是曲泽慧(@zququ),目前在深圳(ICBI,BCBDI,SIAT)任职助理研究员。


病毒学、免疫学及结构生物学背景,可以在 RG 上找到我已发表的论文

本站自2019年7月已访问web counter

python 类和对象 (3) 继承 super函数 多重继承 钻石继承

整理自小甲鱼鱼C论坛

继承

继承的语法为:

class DerivedClassName(BaseClassName):

注意的是,如果子类中定义了与父类相同的方法或者属性,会自动覆盖父类的方法或者属性。

例如如下代码:

import random as r
class Fish:
    def __init__(self):
        self.x = r.randint(0, 10)
        self.y = r.randint(0, 10)
    def move(self):
        self.x -= 1
        print(`我的位置是: `, self.x, self. y)

class Carp(Fish):
    pass

class Shark(Fish):
    def __init__(self):
        self.hungry = True

    def eat(self):
        if self.hungry:
            print("吃")
            self.hungry = False
        else:
            print("不吃")

运行代码后,在Carp和Shark类中分别调用move()方法,观察是否能运行。

>>>fish = Fish()
>>>fish.move()
我的位置是5 10
>>>shark = Shark()
>>>shark.move()
ArributeError: 'Shark' object has no atrribute 'x'

分析原因就是因为在Shark类中,重新定义了构造方法,覆盖了父类Fish的move()方法。

解决方法有两个:

方法1. 在定义Shark的构造方法时,调用未绑定的父类方法

class Shark(Fish):
    def __init__(self):
        Fish.__init__(self)
        self.hungry = True

或者

calss Shark(Fish):
    def __init__(self):
        Fish.__init__(shark)
        self.hungry = True

需要注意的是,这个self并不是父类Fish的实例对象,而是子类的实例对象,而题目所谓的未绑定的父类方法意思就是,并不绑定父类的实例对象,而是直接使用子类的实例对象。

方法2. 使用super函数

super 函数能够自动找到基类的方法,而不需要给出任何基类的名字,会自动找出所有基类以及对应的方法。用法如下:

class Shark(Fish):
    super().__init__()
    self.hungry = True

多重继承

多重继承的语法是:

class DerivedClassName(Base1, Base2, Base3):

举个例子:

class Base1:
    def foo1(self):
        print("foo1")
class Base2:
    def foo2(self):
        print("foo2")
class C(Base1, Base2):
    pass

>>>c = C()
>>>c.foo1()
foo1
>>>c.foo2()
foo2

值得注意的是多重继承问题尽量避免使用,会出现钻石继承问题。

钻石继承

钻石继承是多重继承的陷阱,如下代码:

class A():
   def __init__(self):
       print("进入A…")
       print("离开A…")

class B(A):
   def __init__(self):
       print("进入B…")
       A.__init__(self)
        print("离开B…")
        
class C(A):
    def __init__(self):
        print("进入C…")
        A.__init__(self)
        print("离开C…")

class D(B, C):
    def __init__(self):
        print("进入D…")
        B.__init__(self)
        C.__init__(self)
        print("离开D…")

>>> d = D()
进入D
进入B
进入A
离开A
离开B
进入C
进入A
离开A
离开C
离开D

如下图所示: figure1

因为从D类继承的方式有通过A类为基类的B类,以及通过A类为基类的C类两个方式继承。这样在调用D类时,就会两个词进入A类。如果这时加入计数器会发生计数错误的情况

解决钻石继承的方法可以使用Method Resolution Order(MRO) 方法解析顺序的方法,以及C3算法。

MRO顺序,就是在避免同一类被调用多次的前提下,使用广度优先和左到右的原则去寻找需要的属性和方法。

C3算法确保同一个类只会被搜寻一次,如果一个属性或者方法在D类中没有被找到,Python就会搜索B类,然后搜索C类,如果都没有找到,会继续搜索B的基类A,如果还没有找到会抛出’AttributeError’异常。

 #方法为类.__mro__获得MRO的顺序
>>>D.__mro__
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
 #object是所有类的基类

或者利用super函数解决。

class A():
    def __init__(self):
        print("进入A…")
        print("离开A…")

class B(A):
    def __init__(self):
        print("进入B…")
        super().__init__()
        print("离开B…")
        
class C(A):
    def __init__(self):
        print("进入C…")
        super().__init__()
        print("离开C…")

class D(B, C):
    def __init__(self):
        print("进入D…")
        super().__init__()
        print("离开D…")

>>> d = D()
进入D
进入B
进入C
进入A
离开A
离开C
离开B
离开D

注意1:构造方法不能return值,否则报错

TypeError: __init__() should return None, not 'str'

注意2:当子类方法覆盖了父类方法时,不会删除父类方法,只是该子类看不到父类方法了而已

注意3:当我们想故意覆盖掉父类的方法时,可以定义一个方法直接pass掉,如下:

class Bird:
    def fly(self):
        print("fly")

class Penguin(Bird):
    def fly(self):
        pass

练习:定义一个点(Point)类和直线(Line)类,使用getLen方法可以获得直线长度

提示: Python中开根号使用math模块中的sqrt函数

import math
class Point():
    def __init__(self, valX, valY):
        self.x = valX
        self.y = valY
    def getX(self):
        return self.x
    def getY(self):
        return self.y

class Line():
    def __init__(self, pointA, pointB):
        self.diffX = pointA.getX() - pointB.getX()
        self.diffY = pointA.getY() - pointB.getY()

    def getLen(self):
        return math.sqrt(self.diffX * self.diffX + self.diffY * self.diffY)

pointA               = Point(1, 1)
pointB               = Point(4, 5)
line                 = Line(pointA, pointB)
print(line.getLen())

本例子中获得的经验:

  1. 定义的类可以作为后面类的参数
  2. 注意math.sqrt的用法
  3. 在python中不能用”^2”来表示平方,平方用”**2”来表示
Last One

The interaction between the electron and the sample

Let’s firstly try to analysis the process, during which the single primary electron shot by the high velocity hits at one atom of the sample.There were three situations:Situation 1. As seen in the (a). The primary electron is unscattered, and noth...…

cryo-EMMore
Next One

汇编语言 7.10 不同寻址方式的灵活运用 (1)

7.10 不同寻址方式的灵活运用(1)之前有介绍过几种定位内存地址的方法(寻址方式): [idata]用一个常量来表示内存地址,可用于直接定义一个内存单元,注意的是,单独使用[idata]只能在debug中被正确识别,如果在masm中,需要表明[idata]对应的段,即ds:[idata]。 [bx]用一个变量来表示内存地址,可用于间接定位一个内存单元。其使用时不用表明对应的段,默认[bx]就是在ds中。 [bx+idata]。 [bx+si]。 [bx+si+idata]用两个...…

汇编语言More