Python-面向对象之单继承

类的继承

基本概念

面向对象三要素之一,继承inheritance,人类和猫类都是继承自动物 类。 

个体继承自父母,继承了父母一部分特征,但也可以有自己的个性特征。

在面向对象的世界中,从父类继承,就可以直接拥有父类的属性和方法,这样可以减少代码、多复用。

子类也可以自定义自己的属性和方法。

class Animal:
    def shout(self):
        print("Animal shouts")

a = Animal()
a.shout()

class Cat:
    def shout(self):
        print("Cat shouts")
c = Cat()
c.shout()

#输出
Animal shouts
Cat shouts   

上面2个类虽然有关系,但是定义是并没有这种关系,而且是各自定义,动物类和猫类都会叫,但是他们的叫法有区别,所以分别定义。

class Animal:
    def __init__(self,name):
        self.__name = name
    def shout(self):
        print("{} shouts".format(self.__class__.__name__))
    @property
    def name(self):
        return self.__name
a = Animal("monster")
a.shout()
print(a.__dict__.items())

class Cat(Animal):
    pass
cat = Cat('garfield')
cat.shout()
print(cat.name)
print(cat.__dict__.items())

class Dog(Animal):
    pass

dog = Dog("dongdong")
dog.shout()
print(dog.name)
print(dog.__dict__.items())

#输出
Animal shouts
dict_items([('_Animal__name', 'monster')])
Cat shouts
garfield
dict_items([('_Animal__name', 'garfield')])
Dog shouts
dongdong
dict_items([('_Animal__name', 'dongdong')])

上例可以看出,通过继承、猫类、狗类不用写代码,直接继承父类的属性和方法。

 

继承

class Cat(Animal)这种形式就是从父类继承,括号中写上继承的类的列表。

继承可以让子类从父类获取特征(属性和方法)

 

父类

Animal是Cat的父类,也称之为基类或超类。

 

子类

Cat就是Animal的子类,也称之为派生类。

 

定义

格式如下

class 子类名(基类1[基类2,.....])
    语句块

如果类定义时,没有基类列表等同于继承自object。在Python3中,object类是所有对象的根基类。

class A:
    pass
#等价于
class A(object):
    pass

注意,上例中在Python2,写法是不同的。

Python支持多继承,继承也可以多级。

查看继承的特殊属性和方法有如下:

特殊属性和方法          含义

__bases__            类的基类元组

__base__              类的基类元组的第一项

__mro__             显示方法的查找顺序,基类的元组

mro()                同上,返回列表

__subclasses__()         类的子类列表

class Animal:
    def __init__(self,name):
        self.__name = name

    def shout(self):
        print("{} shouts".format(self.__class__.__name__))

    @property

    def name(self):
        return self.__name

class Cat(Animal):
    pass
class Dog(Animal):
    pass
a = Animal("Pig")
a.shout()

cat = Cat("cat")
cat.shout()

dog = Dog("Dog")
dog.shout()

print(Animal.__bases__)
print(a.__class__.__bases__)
print(Animal.__base__)
print("-" * 30)

print(Cat.__bases__)
print(Cat.__base__)
print(Cat.__mro__)
print(Cat.mro())

print(int.__init_subclass__())
print(bool.mro())


#输出
Animal shouts
Cat shouts
Dog shouts
(<class 'object'>,)
(<class 'object'>,)
<class 'object'>
------------------------------
(<class '__main__.Animal'>,)
<class '__main__.Animal'>
(<class '__main__.Cat'>, <class '__main__.Animal'>, <class 'object'>)
[<class '__main__.Cat'>, <class '__main__.Animal'>, <class 'object'>]
None
[<class 'bool'>, <class 'int'>, <class 'object'>]

Python2.2之前是没有共同的祖先,之后引入了object类,它是所有类的共同祖先类object。

新式类都是继承自object的,新式类可以使用super。

 

继承中的访问控制

class Animal:
    __COUNT = 100
    HEIGHT = 0

    def __init__(self,age,weight,height):
        self.__COUNT += 1
        self.age = age
        self.__weight = weight
        self.HEIGHT = height

    def eat(self):
        print("{} eat".format(self.__class__.__name__))

    def __getweight(self):
        print(self.__weight)

    @property
    def getweight(self):
        return self.__weight


    @classmethod
    def showcount1(cls):
        print(cls)
        print(cls.__dict__)
        print(cls.__COUNT)

    @classmethod
    def __showcount2(cls):
        print(cls.__COUNT)


    def showcount3(self):
        print(self.__COUNT)

class Cat(Animal):
    NAME = "CAT"
    __COUNT = 200

    @property
    def getcatcount(self):
        return self.__COUNT

c = Cat(3,15,30)
c.eat()
print(c.HEIGHT)
print(c.getweight)
c._Animal__getweight() #  使用魔术方法访问
# print(c.__COUNT) #私有属性无法访问
# print(c._Cat__COUNT) # 200
# print(c._Animal__COUNT) # 101
print(c.getcatcount)


c.showcount1()
print("*" * 30)
c._Animal__showcount2()
print("*" * 30)
c.showcount3()

print(c.NAME) # CAT
print(Animal.__dict__)
print(Cat.__dict__)
print(c.__dict__)
print(c.__class__.mro())


#输出
Cat eat
30
15
15
200
<class '__main__.Cat'>
{'__module__': '__main__', 'NAME': 'CAT', '_Cat__COUNT': 200, 'getcatcount': <property object at 0x0000016EA763C548>, '__doc__': None}
100
******************************
100
******************************
101
CAT
{'__module__': '__main__', '_Animal__COUNT': 100, 'HEIGHT': 0, '__init__': <function Animal.__init__ at 0x0000016EA76FCF28>, 'eat': <function Animal.eat at 0x0000016EA7710048>, '_Animal__getweight': <function Animal.__getweight at 0x0000016EA77100D0>, 'getweight': <property object at 0x0000016EA7606688>, 'showcount1': <classmethod object at 0x0000016EA7711080>, '_Animal__showcount2': <classmethod object at 0x0000016EA77110B8>, 'showcount3': <function Animal.showcount3 at 0x0000016EA77102F0>, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__doc__': None}
{'__module__': '__main__', 'NAME': 'CAT', '_Cat__COUNT': 200, 'getcatcount': <property object at 0x0000016EA763C548>, '__doc__': None}
{'_Animal__COUNT': 101, 'age': 3, '_Animal__weight': 15, 'HEIGHT': 30}
[<class '__main__.Cat'>, <class '__main__.Animal'>, <class 'object'>]

从父类继承,自己没有的,就可以从父类中找。

私有的都是不可以访问的,但是本质上依然是改了名称放在这个属性所在类的实例__dict__中,知道这个新的名称就可以访问到这个私有属性,魔术方法,慎用。

总结:

集成时,公有成员,子类和实例都可以随意访问;私有成员被影藏,子类和实例不可直接访问,但私有变量所在类内的方法中可以访问这个私有变量。

 

实例属性的查找顺序:

实例的__dict__ --> 类的__dict__-->如果有继承--> 父类的__dict__ --> object 

 # [<class '__main__.Cat'>, <class '__main__.Animal'>, <class 'object'>]

如果搜索这些地方后没有找到就会抛异常,先找到就立即返回。

 

方法的重写、覆盖override

class Animal:
    def shout(self):
        print("Animal shouts")

class Cat(Animal):
    def shout(self): #覆盖了父类的方法
        print("Cat shouts")
a = Animal()
a.shout()

cat = Cat()
cat.shout()

print(a.__dict__)
print(cat.__dict__)
print(Cat.__dict__)
print(Animal.__dict__)

#输出
Animal shouts
Cat shouts
{}
{}
{'__module__': '__main__', 'shout': <function Cat.shout at 0x000001925792F0D0>, '__doc__': None}
{'__module__': '__main__', 'shout': <function Animal.shout at 0x000001925792F048>, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__doc__': None}

Cat能否覆盖自己的方法,可以。

Cat中能否对父类的方法做个增强,不需要完全重写,可以。

class Animal:
    def shout(self):
        print("Animal shouts")

class Cat(Animal):
    def shout(self):
        print("Cat shouts")
    def shout(self):
        print(super())
        print(super(Cat,self))
        super().shout()
        # super(Cat,self).shout() #等价于super().shout()
        # self.__class__.__base__.shout(self)  #等价上面两种写法,不推荐使用
        print("maio miao miao")

a = Animal()
a.shout()

cat = Cat()
cat.shout()

#输出
Animal shouts
<super: <class 'Cat'>, <Cat object>>
<super: <class 'Cat'>, <Cat object>>
Animal shouts
maio miao miao

super()可以访问到父类的类属性。

class Animal:
    @classmethod
    def class_method(cls):
        print('class_method_animal')

    @staticmethod
    def static_method():
        print('static_method_animal')

class Cat(Animal):
    @classmethod
    def class_method(cls):
        print('class_method_cat')

    @staticmethod
    def static_method():
        print('static_method_cat')

c = Cat()
c.class_method()
c.static_method()

print(Cat.__dict__)
print(Animal.__dict__)
print(c.__class__.mro())

#输出
class_method_cat
static_method_cat
{'__module__': '__main__', 'class_method': <classmethod object at 0x0000010454AD6B38>, 'static_method': <staticmethod object at 0x0000010454AD6C88>, '__doc__': None}
{'__module__': '__main__', 'class_method': <classmethod object at 0x0000010454AD6B00>, 'static_method': <staticmethod object at 0x0000010454AD6A90>, '__dict__': <attribute '__dict__' of 'Animal' objects>, '__weakref__': <attribute '__weakref__' of 'Animal' objects>, '__doc__': None}
[<class '__main__.Cat'>, <class '__main__.Animal'>, <class 'object'>]

类方法和静态方法是特殊的方法,也是类属性,这些方法都可以覆盖,属性字典的搜索顺序一致。

 

继承时使用初始化

class A:
    def __init__(self,a):
        self.a = a

class B(A):
    def __init__(self,b,c):
        self.b = b
        self.c = c
    def printf(self):
        print(self.b)
        print(self.a)

f = B(200,300)
print(f.__class__.__bases__)
print(f.__dict__)
f.printf()

上例代码可知:

如果B定义是声明继承自类A,则在类B中__bases__中是可以看到类A

但是这个和是否调用类A的构造方法是两回事。

如果类B中调用了父类A的构造方法,就可以拥有父类的属性

class A:
    def __init__(self,a,d=10):
        self.a = a
        self.__d = d
class B(A):
    def __init__(self,b,c):
        A.__init__(self,b+c,b-c)
        # super().__init__(b+c,b-c)
        self.b = b
        self.c = c
    def printf(self):
        print(self.b)
        print(self.a)
f = B(20,30)
f.printf()

##输出
20
50

作为好习惯,如果父类定义了__init__方法,你就应该在子类的init中调用它,子类如何自动调用父类的__init__?

示例1:

class A:
    def __init__(self):
        self.a1 = 'a1'
        self.b1 = 'b1'
        print('init A')

class B(A):
    pass

b = B()

print(b.__dict__)

#输出

init A
{'a1': 'a1', 'b1': 'b1'}

B实例的初始化会自动调用基类的A的__init__方法

示例2:

class A:
    def __init__(self):
        self.a1 = 'a1'
        self.b1 = 'b1'
        print('init A')

class B(A):
    def __init__(self):
        self.b1 = 'b1'
        print("init B")

b = B()
print(b.__dict__)

#输出
init B
{'b1': 'b1'}

B实例一旦定义了初始化__init__方法,就不会自动调用父类的初始化方法__init__,需要手动调用。

class A:
    def __init__(self):
        self.a1 = 'a1'
        self.b1 = 'b1'
        print('init A')

class B(A):
    def __init__(self):
        A.__init__(self)
        self.b1 = 'b1'
        print("init B")

b = B()
print(b.__dict__)

#输出
init A
init B
{'a1': 'a1', 'b1': 'b1'}

如何正确的初始化? 看如下示例

class Animal:
    def __init__(self,age):
        print('init in animal')
        self.age = age

    def show(self):
        print(self.age)

class Cat(Animal):
    def __init__(self,age,weight):
        print('init in cat')
        self.age = age + 1
        self.weight = weight
c = Cat(10,5)
c.show()

#输出
init in cat
11

上例分析过,不会调用父类的__init__方法。在子类的__init__方法中,应该显示调用父类的__init__方法。

class Animal:
    def __init__(self,age):
        print('init in animal')
        self.age = age

    def show(self):
        print(self.age)

class Cat(Animal):
    def __init__(self,age,weight):
     #调用父类的__init__方法顺序有时决定着show方法的结果 super().
__init__(age) print('init in cat') self.age = age + 1 # super().__init__(age) #init in cat init in animal 10 self.weight = weight c = Cat(10,5) c.show() #输出 init in animal init in cat 11

注意,调用父类的__init__方法,出现在不同的位置,结果可能不同。

class Animal:
    def __init__(self,age):
        print('init in animal')
        self.__age = age

    def show(self):
        print(self.__age)

class Cat(Animal):
    def __init__(self,age,weight):
        super().__init__(age)
        print('init in cat')
        self.__age = age + 1
        # super().__init__(age)
        self.weight = weight
c = Cat(10,5)
c.show()
print(c.__dict__)


#输出
init in animal
init in cat
10
{'_Animal__age': 10, '_Cat__age': 11, 'weight': 5}

上例中打印10,原因看__dict__就知道,因为父类Animal的show方法中的__age会被解释器改名为_Animal__age

解决办法:一个原则,自己的私有属性就该自己的方法读取和修改,不要借助其他类的方法,即使是父类或子类的方法。

 

单继承

上面示例中,类的继承列表只有一个类,这种称之为单一继承。

OCP原则:多用继承,少修改

继承的用途,在子类上实现对基类的增强,实现多态。

 

多态

在面向对象中,父类、子类通过继承联系在一起,如果可以通过一套方法,就可以实现不同的表现,就是多态。

posted @ 2020-05-17 10:56  Alrenn  阅读(269)  评论(0)    收藏  举报