chapter8.2、面向对象三要素--继承

继承Inheritance

单继承

个体继承自父母,继承父母的属性,但也会有自己的属性

面向对象里,属性和方法可以从从父类继承,这样能减少代码,能复用就复用,子类复用的属性不能表达他自己的属性时,再为子类增添属性。

父类father,也称为   基类base超类super,它们意思相同

子类son派生类sub

 

class Creatures:
    def __init__(self,name):
        self._name = name
    def shout(self):
        print('{} shout'.format(self.__class__.__name__))
    @property
    def name(self):
        return self._name

a = Creatures('monster')
a.shout()

class Cat(Creatures):
    pass

cat = Cat("garfield")
cat.shout()
print(cat.name)

class Pig(Creatures):
    pass

pig = Pig('peiqi')
pig.shout()
print(pig.name)

 

表达方法 :

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

class Person:
    pass
class Person(object)
    pass

以上两种等价

object 祖先,根基类,唯一没有父类的,所有的类都继承自object,python3中全部类最终继承自object,如果不写父类,父类就是object,Python3之前不是

python 支持多继承,查看继承的特殊属性和方法有

__base__   类的基类,只往上找一层,返回类对象

__bases__  类的基类元祖,不会找到object,但在中间的类都会包装成元组返回

__mro__   返回继承路线,返回所有元祖,包括object,也可以mro() 

mor()  方法,返回的是列表

__subclasses__()    类的子类列表

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)
        
    @classmethod
    def showcount1(cls):
        print(cls)
        #print(cls.__dict__)
        print(cls.__COUNT,'--------1')
        
    @classmethod
    def __showcount2(cls):
        print(cls.COUNT,'---------2')
        
    def showcount3(self):
        print(self.__COUNT,'-------3')
        
class Pig(Animal):
    NAME = 'PAGE'
    __COUNT = 300

#p = Pig()###创建实例访问的属性为Pig的类,Pig没有,就去父类Animal里找,要求有参数
p = Pig(5,50,30)#创建实例
p.eat()##实例所属的类Pig没有该方法,但是Animal有,调用
print(p.HEIGHT)##实例自身的属性
#print(p.__COUNT)##对象的属性是储存在自己字典里的,只不过换了名字
#p.__getweight()##该方法属于它的类,但是类隐藏了该属性,所以调不到
p.showcount1()##该方法属于类的父类,且未隐藏,可以调,返回100,该方法是Animal类调用的__count,不会去实例里边找,所以返回类里的隐藏变量100
#p.showcount2()##该方法属于它的类,但是类隐藏了该属性,所以调不到
p.showcount3()##返回101,对象调用类的方法,首先找的是对象的字典,谁调用,去谁那找

##信息都藏在字典里!
print('{}'.format(Animal.__dict__))
print('{}'.format(Pig.__dict__))
print(p.__dict__)
print(p.__class__.mro())

 

继承中的访问控制

只要是继承,自己没有的,就可以到父类中找,私有的和类相关,私有属性的更改就要在类下更改

私有的都是不可访问的,但本质时放在了属性或类的字典__dict__中,可以找到,黑魔法,慎用,

谁调用谁的self和cls

继承时,公有的,子类和实例都可以随意访问;隐藏的,子类和实例都不可直接访问,私有不会和子类父类分享,只有自己用,自己类下的方法都可以访问这个私有变量

子类实例都不可访问私有属性,私有一旦出了类,就不能访问了

其他语言有的严格限制访问

 

方法的重写,覆盖override

继承修改了属性,就是重写,自己类下的方法也可以覆盖

class Creatures:
    def shout(self):
        print('Animal shout')

class Pig(Creatures):
    def shout(self):
        print('hengheng')
    
    def shout(self):
        print(super())
        print(super(Pig,self))##和上一种相同
        super().shout()##子类调父类的方法,以下两种与之相同
        super(Pig,self).shout()
        self.__class__.__base__.shout(self)##不推荐使用此方法
        
c = Creatures()
c.shout()
pig = Pig()
pig.shout()

print(c.__dict__)
print(pig.__dict__)
print(Creatures.__dict__)
print(Pig.__dict__)

子类可以使用super()访问父类的属性,super()  等价于 super(父类名,self)

静态方法和类方法都与之相同,都可以覆盖

class Creatures:
    @classmethod
    def class_method(cls):
        print('class method creatures')

    @staticmethod
    def static_method():
        print('static method creatures')


class Pig(Creatures):
    @classmethod
    def class_method(cls):
        print('class method pig')

    @staticmethod
    def static_method():
        print('static method pig')


p = Pig()
p.class_method()
p.static_method()

 

继承中的初始化

本身有初始化方法,初始化不会自动调父类的初始化方法。

如果没有,就会调父类的初始化方法 ,父类有init方法,子类也调用是好习惯,不调用实例可能会少父类的实例的属性

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

class Cat(Animal):
    def __init__(self,age,weight):
        super().__init__(age)##放在此处,age返回11,先执行Animal的初始化,在执行Cat的age,所以c的age是11
        print('cat init')
        self.age  = age +1##此处执行时age为10加11
        self.weight = weight
        #super().__init__(age)
     ##如果放在此处,返回的为10,首次执行Cat的初始化,将c的age设置为了11,再执行Animal的初始化,传入的是参数age,不是c的age,所以又覆盖了c的属性,输出10
        
c = Cat(10,5)
c.show()

调用父类的初始化方法的顺序会影响实例的属性,应当注意

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

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

实例调用类的私有方法调用时产生的属性放在实例中,但是名字与类有关,最好不要用。

父类的初始化方法调用后,使用的实例是当前类的实例,父类不产生实例,子类调用他父类的的方法

主要原则:自己的私有属性,就用自己的方法读取修改,不要使用父类和派生类或者其他类的方法

 

 

python的不同版本的类

python2.2之前,没有类的共同祖先,之后,引入了object 所有类的共同祖先,

python2.2之后,分为古典类和新式类。

python3中都是新式类,都继承自object

dir 所有属性的列表

 

多继承

OCP原则,多‘继承’,少修改

在子类上对基类的增强,实现多态,

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

一个类继承自多个类就是多继承,它将具有多个类的特征

多继承弊端

复杂性,只要不是单一继承,就会面对复杂性这一问题

二义性,如果继承自两个类,两个类里都有相同的方法,子类该继承那个方法,就产生了二义性

C++支持多继承,Java舍弃了多继承

解决途径

排除二义性,要遍历,方法是深度优先,或广度优先

菱形选择问题,类D继承自B和C,B和C又继承自A,当然,类的继承顺序可以比这更复杂

2.2之前,最早是深度优先,先根再左在右

2.2古典类继承路线中重复的以最后为准,但不能解决问题

python2.3 之后,C3算法,可以解决二义性问题,还可以抛异常,解决了继承的单调性,出现二义性就阻止它,求得的MRO本质就是为了线性化,且确定顺序,

多继承使用时要注意

python语法是可以多继承,但是它是解释执行,只有执行到时才会发现错误。

尽量避免多继承,不论语言是否支持

团队协作开发,引入多继承,代码可能会不可控

python开发要求较高,只有运行时才能提示错误,所以在编写时要团队遵守相同的规矩

 

Mixin

抽象方法可以不实现,继承实现,父类不具体实现,子类来覆盖它

基类中只定义,不实现,称为‘抽象方法’,在Python中,如果采用这种定义方式,子类也可以不实现,直到子类使用该方法时才报错。

import math
import json
import msgpack

class Shape:
    @property
    def area(self):
        raise NotImplemented('weishixian')

class Triangle(Shape):
    def __init__(self, a, b, c):
        self.__a = a
        self.__b = b
        self.__c = c

    @property
    def area(self):
        p = (self.__a + self.__b + self.__c) / 2
        ret = math.sqrt(p*(p - self.__a)*(p - self.__b)*(p - self.__c))
        return ret

class Square(Shape):
    def __init__(self,a,b):
        self.__a = a
        self.__b = b

    @property
    def area(self):
        return self.__a*self.__b

class Circle(Shape):
    def __init__(self,r):
        self.__r = r

    @property
    def area(self):
        return math.pi*(self.__r**2)

t = Triangle(3,4,5)
print(t.area)
s = Square(2,2)
print(s.area)
r = Circle(1)
print(r.area)

 

临时注入功能,可以用装饰器注入,装饰器为类灵活的增添功能;也可以Mixin,用Mixin时,混入谁,把谁放在前边,Mixin覆盖其他的属性,缺少啥补啥,一般不会太复杂

class SerializableMixin:##Mixin类
    def dump(self,t = 'json'):##增添的功能,可序列化
        if t == 'json':
            return json.dumps(self.__dict__)
        if t == 'msgpack':
            return msgpack.dumps(self.__dict__)
        else :
            return "not realize serialize"

class SerializableCircleMixin(SerializableMixin,Circle): pass#多继承,放在第一位

scm = SerializableCircleMixin(4)
print(scm.area)
s = scm.dump('msgpack')
print(s)
j = scm.dump('json')
print(j)

 

子类指向父类,画图的规则

继承的层次多了,会笨拙,

Mixin,混入其他功能,Mixin带来了其他属性和方法,Mixin本质是多继承实现的

Mixin体现的是组合的·设计思想,模式,

在面向对象的设计中,复杂功能可以抽取出来,为其他类提供功能,可以装饰或者Mixin,

从设计角度来说,多组合,少继承,为了改变mro列表

使用原则

Mixin类中不能显式的出现初始化方法__init__

Mixin通常不能单独工作,因为它是为混入其他类增加功能的

Mixin的祖先只能是Mixin类,只是为了增添功能,不是强制,但这是原则,一般都这样做

注意Mixin类通常在继承列表的第一个位置

两种方式都可以,如果要继承就使用Mixin,

 

PEP是python的增强提案,Python Enhancement Proposals

新特性,PEP 572,2018年7月,Guido van Rossum,python之父卸任了BDFL(benevolent dictator for life),没有指定继任者,

PEP 0 文档索引
PEP 1 协议指南
PEP 8 代码规范!!!

Python社区,新的规范的原始文档都在这里,可以访问

https://www.python.org/dev/peps/pep-0008/

 

posted on 2018-09-17 21:21  Riper  阅读(210)  评论(0编辑  收藏  举报

导航