类的继承

类的继承

基本概念
  • 面向对象三要素之一,继承Inheritance
  • 举例:
    • 人类和猫类都继承自动物类
    • 个体继承自父母,继承了父母的一部分特征,但也可以有自己的个性
    • 在面向对象的世界中,以父类继承,就可以直接拥有父类的属性和方法,这样可以减少代码、多复用。子类可以定义自己的属性和方法
class Animal:
def shout(self):
print(“Animal shout”)
a = Animal()
a.shout()
class Cat:
def shout(self):
print(“Cat shout”)
c = Cat()
c.shout()
  • 上面的类虽然有关系,但是定义时并没有建立这种关系,而是各自完成了定义,动物类和猫类都有吃,但是他们的吃有区别,所以分别定义
class Animal:
def __init__(self,name):
self.__name = name
def shout(self):
print(“{} shout”.format(self.__class__.__name__))
@property#error cat’t set attrbude此方法只用于只读,如果不加下划线则无法传入name
def name(self):
return self.__name
a = Animal(‘monster’)
a.shout()
class Cat(Animal):
pass
cat = Cat(‘garfeild’)
cat.shout()
print(cat.name)
class Dog(Animal):
pass
dog = Dog(‘labuladuo’)
dog.shout()
print(dog.name)
  • 上例可以看出,通过继承,猫类、狗类不用写代码,直接继承了父类的属性和方法
继承
  • class Cat(Animal)这种形式就是从父类继承,括号中写上继承的类的列表
  • 继承可以让子类从父类获取特征(属性和方法)
    父类
  • Animal就是Cat的父类,也称为基类、超类
    子类
  • Cat就是Animal的子类,也称派生类
定义
  • class 子类名(基类1[,基类2,……])
    • 语句块
  • 如果类定义时,没有基类列表,等同于继承自object。在Python3中,object类是所有对象的根基
  • Python支持多继承,继承也可以多级
  • 查看继承的特殊属性和方法
特殊属性和方法
含义
示例
__base__
类的基类
__bases__
类的基类元祖
__mro__
显示方法查找顺序,基类的元祖
mro()方法
同上
ini.mro()
__subclasses__
类的子类列表
int.__subclasses__()
print(Cat.mro())#Cat为类
print(Cat.__mro__)#两种方法等效
继承的访问控制
# class Animal:
# def shout(self):
# print(“Animal shout”)
# a = Animal()
# a.shout()
#
# class Cat:
# def shout(self):
# print(“Cat shout”)
# c = Cat()
# c.shout()
class Animal:
__COUNT = 0
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.__COUNT)
@classmethod
def __showcount2(cls):
print(cls.__COUNT)
class Cat(Animal):
NAME = ‘CAT’
#c = Cat()#init函数参数错误
c = Cat(3,4,15)
print(c.HEIGHT)
#print(c.__COUNT)#私有的不可访问
#c.__showweight()
c.showcount1()
#c.__showcount2()
print(c.NAME)
print(“{}”.format(Animal.__dict__))
print(“{}”.format(Cat.__dict__))
print(c.__dict__)#显示的为实例初始化时传入的参数即init接收的值
print(c.__class__.mro())#先找自己如果没有则找父类
————–
0
CAT
{‘__dict__’: <attribute ‘__dict__’ of ‘Animal’ objects>, ‘HEIGHT’: 0, ‘__weakref__’: <attribute ‘__weakref__’ of ‘Animal’ objects>, ‘__doc__’: None, ‘_Animal__COUNT’: 0, ‘_Animal__showcount2’: <classmethod object at 0x000000A8ADAB89B0>, ‘eat’: <function Animal.eat at 0x000000A8ADAAB8C8>, ‘_Animal__getweight’: <function Animal.__getweight at 0x000000A8ADAAB950>, ‘__module__’: ‘__main__’, ‘__init__’: <function Animal.__init__ at 0x000000A8ADAAB840>, ‘showcount1’: <classmethod object at 0x000000A8ADAB8978>}
{‘__doc__’: None, ‘__module__’: ‘__main__’, ‘NAME’: ‘CAT’}
{‘HEIGHT’: 15, ‘age’: 3, ‘_Animal__COUNT’: 1, ‘_Animal__weight’: 4}
[<class ‘__main__.Cat’>, <class ‘__main__.Animal’>, <class ‘object’>]
  • 父类继承,自己没有的,就可以到父类中找
  • 私有的都是不可以访问的,但是本质上依然是改了名称放到这个属性所在类的__dict__中。知道这个新名称就可以直接找到这个隐藏变量,慎用
总结:
继承时,公有的,子类和实例都可以随意访问;私有成员被隐藏,子类和实例不可以直接访问。只有私有变量所在的类内的方法可以访问这个私有变量
Python通过自己一套实现,实现和其他语言一样的面向对象的继承机制
属性的查找顺序:
实例的__dict__ —>类__dict__ —>父类__dict__
如果搜索这些地方后没有找到就会抛异常
方法的重写、覆盖override
class Animal:
def shout(self):
print(‘Animal shout’)
class Cat(Animal):
def shout(self):
print(‘miao’)
a = Animal()
a.shout()
c = Cat()
c.shout()
print(a.__dict__)
print(c.__dict__)#由于没有传递任何参数,故返回空字典
print(Animal.__dict__)
print(Cat.__dict__)
———-
Animal shout
miao##覆盖了父类的shout方法
{}
{}
{‘__module__’: ‘__main__’, ‘__doc__’: None, ‘__dict__’: <attribute ‘__dict__’ of ‘Animal’ objects>, ‘__weakref__’: <attribute ‘__weakref__’ of ‘Animal’ objects>, ‘shout’: <function Animal.shout at 0x000000BE11C4B730>}
{‘__module__’: ‘__main__’, ‘__doc__’: None, ‘shout’: <function Cat.shout at 0x000000BE11C4B7B8>}
class Animal:
def shout(self):
print(‘Animal shout’)
class Cat(Animal):
def shout(self):#覆盖了父类的方法
print(‘miao’)
def shout(self):#覆盖了自身的方法,显式调用了父类的方法
print(super())
print(super(Cat,self))
super().shout()#super()可调用父类的方法
super(Cat,self).shout()#等价于super()
self.__class__.__base__.shout(self)#不推荐
a = Animal()
a.shout()
c = Cat()
c.shout()
print(a.__dict__)
print(c.__dict__)
print(Animal.__dict__)
print(Cat.__dict__)
———————-
Animal shout
<super: <class ‘Cat’>, <Cat object>>
<super: <class ‘Cat’>, <Cat object>>
Animal shout
Animal shout
Animal shout
{}
{}
{‘__weakref__’: <attribute ‘__weakref__’ of ‘Animal’ objects>, ‘__doc__’: None, ‘__dict__’: <attribute ‘__dict__’ of ‘Animal’ objects>, ‘shout’: <function Animal.shout at 0x000000EA0FDDB730>, ‘__module__’: ‘__main__’}
{‘__doc__’: None, ‘__module__’: ‘__main__’, ‘shout’: <function Cat.shout at 0x000000EA0FDDB840>}
super()可以访问到父类的属性,原理未知
class Animal:
@classmethod
def class_method(cls):
print(‘class_method_anmal’)
@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()
a = Animal()
a.class_method()
a.static_method()
c.class_method()
c.static_method()
  • 方法覆盖,原理一样,属性字典的搜索顺序
继承中的初始化
class A:
def __init__(self,a):
self.a = a
class B(A):
def __init__(self,b,c):
self.b = b
self.c = c
def printv(self):
print(self.b)
print(self.a)#由于此时B的init已经覆盖掉A的init所以a没有赋值
f = B(200,300)
print(f.__dict__)
print(f.__class__)
print(B.__class__.__bases__)
f.printv()
—————————
{‘c’: 300, ‘b’: 200}
(<class ‘object’>,)
200
  • 上列代码可知:
    • 如果类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)
self.b = b
self.c = c
def printv(self):
print(self.b)
print(self.a)
f = B(200,300)
print(f.__dict__)
print(B.__class__.__bases__)
f.printv()
——————–
{‘_A__d’: -100, ‘a’: 500, ‘b’: 200, ‘c’: 300}#可以看到f字典中的键值,d为隐藏属性并且属于A类所以显示这样
(<class ‘object’>,)
200
500
  • 作为好习惯,如果父类定义了init,就应该在子类的init中调用它
  • 如果子类中没有定义init,则自动调用父类init,如下:
class A:
def __init__(self,a,d=10):
self.a = a
self.__d = d
class B(A):
def printv(self):
print(self.a)
f = B(200,300)
print(f.__dict__)
print(B.__class__.__bases__)
f.printv()
—————-
{‘_A__d’: 300, ‘a’: 200}
(<class ‘object’>,)
200
  • 如果子类中有init方法则不会自动调用父类的初始化init,需要手动调用
#此测试可以显示实例化后对象的dict,所有self的属性都会被放到字典中
class A:
def __init__(self):
self.a = “a”
self.__d = “d”
print(‘A init’)
class B(A):
def __init__(self):
self.c = ‘c’
print(‘B init’)
A.__init__(self)
f = B()
print(f.__dict__)
—————————–
B init
A init
{‘c’: ‘c’, ‘a’: ‘a’, ‘_A__d’: ‘d’}
  • 下例中打印10,原因看__dict__就知道了,因为父类Animal中的show方法中__age会被解释成_Animal__age,因此显示的是10,而不是11
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
c = Cat(10,5)
c.show()
print(c.__dict__)
————————
Animal init
Cat init
10
{‘_Animal__age’: 10, ‘_Cat__weight’: 5, ‘_Cat__age’: 11}
解决办法:一个原则,自己的私有属性,就该自己的方法读取和修改,不要借助其他类的方法,即使是父类或者派生类的方法

多继承
  • ocp原则:多继承,少修改
  • 继承的用途:增强基类、实现多态
多态
  • 在面向对象中,父类、子类通过继承联系在一起,如果可以通过一套方法,就可以实现不同表现,就是多态
  • 一个类继承自多个类就是多继承,它将具有多个类的特性
  • 弊端:
    • 多继承很好的模拟了世界,因为事物很少是单一继承,但是舍弃简单,必然引入复杂性,带来了多冲突
    • 如同一个孩子继承了来自父母双方的特征,那么到底眼睛像爸还是像妈?应该像谁多一些
    • 多继承的实现会导致编译器设计的复杂度增加,所以很多语言舍弃了多继承
    • C++支持多继承,Java舍弃了多继承
    • Java中,一个类可以实现多个接口,一个接口也可以继承多个接口。java的接口很纯粹,只是方法的声明,继承者必须实现这些方法,就具有了这些能力,就能干什么。
    • 多继承可能会带来二义性,例如,猫和狗都继承自动物类,现在如果一个类多继承了猫和狗类,猫和狗都有了shout方法,子类究竟继承了谁的shout?
  • 解决方案
    • 实现多继承的语言,要解决二义性,深度优先或者广度优先
Python多继承实现
  • class Classname(基类列表):
    • 类体
Alt text
  • 多继承带来路径选择问题,究竟继承哪个父亲的特性呢。
  • Python使用MRO(method resolution order)解决基类搜索顺序问题
    • MRO三个搜索算法:
      • 经典算法,按照定义从左到右,深度优先 2.2之前
        • 上左图的MRO:Myclass->D->B->A->C->A
      • 新式类算法,经典算法的升级,重复的只保留最后一个。2.2
        • 上左图MRO:Myclass->D->B->C->A,object
      • C3算法,在类被创建出来的时候,就计算出一个MRO有序列表,2.3之后,Python3唯一支持的算法
        • 上左图MRO:Myclass->D->B->C->A,object
        • C3算法解决了多继承的二义性
          多继承的缺点
    • 当类很多,继承复杂的情况下,继承路径太多,很难说清什么样的继承路径
    • Python语法是允许多继承,但是Python代码是解释执行,只有执行到的时候才发现错误
    • 团队协作开发,如果引入多继承,代码将不可控
    • 不管编程语言是否支持多继承,都应当避免多继承
    • Python的面向对象,我们看到的太灵活,太开放,所以需要团队守规矩
Mixin
Alt text
#为Document子类提供打印能力
class Printable:
def _print(self):
print(self.content)
class Docment:
def __init__(self,content):
self.content = content
class Word(Docment):
pass
class Pdf(Docment):
pass
class PrintableWord(Printable,Word):
pass
print(PrintableWord.__dict__)
print(PrintableWord.mro())#倒数第二个会找到Docment类
pw = PrintableWord(‘test string’)
pw._print()
————————–
{‘__module__’: ‘__main__’, ‘__doc__’: None}
[<class ‘__main__.PrintableWord’>, <class ‘__main__.Printable’>, <class ‘__main__.Word’>, <class ‘__main__.Docment’>, <class ‘object’>]
test string
  • 分析:
    • 看似不错,如果需要还要提供其他能力,如何继承?
    • 应用于网路哦,文档应该具备序列化的能力,泪上就应该实现序列化
    • 可序列化还可能分为使用pickle,json,messagepack等
    • 这时候发现,类可能太多,继承的方式不是很好了
    • 功能太多,A类需要某几样功能,B类需要另几样功能,很繁琐
  • 装饰器
    • 用装饰器增强一个类,把功能给类附加上去,哪个类需要,就装饰它
def printable(cls):
def _print(self):
print(self.content,’装饰器’)
cls.print = _print
return cls
def test(cls):
cls.test = lambda self:print(‘test’,self.content)
return cls
class Docment:
def __init__(self,content):
self.content = content
class Word(Docment):
pass
class Pdf(Docment):
pass
@test
@printable#PrintableWord=printable(PrintableWord)先继承后装饰
class PrintableWord(Word):
pass
print(PrintableWord.__dict__)
print(PrintableWord.mro())
pw = PrintableWord(‘test string’)
pw.print()
pw.test()
———————–
{‘test’: <function test.<locals>.<lambda> at 0x000000EFA476B8C8>, ‘print’: <function printable.<locals>._print at 0x000000EFA476B7B8>, ‘__module__’: ‘__main__’, ‘__doc__’: None}
[<class ‘__main__.PrintableWord’>, <class ‘__main__.Word’>, <class ‘__main__.Docment’>, <class ‘object’>]
test string 装饰器
test test string
  • 使用装饰器装饰类的好处是:简单方便,在需要的地方动态增加
Mixin
class PrintableMixin:
def print(self):
print(self.content,’Mixin’)
def printable(cls):
def _print(self):
print(self.content,’装饰器’)
cls.print = _print
return cls
class Docment():
def __init__(self,content):
self.content = content
class Word(Docment): pass
class Pdf(Docment): pass
class PrintableWord(PrintableMixin,Word): pass#多继承,先左后右
print(PrintableWord.mro())
print(PrintableWord.__dict__)
pw = PrintableWord(‘test hhahha’)
pw.print()
  • Mixin就是其他类混合进来,同时带来了类的属性和方法
  • 这里看来Mixin类和装饰器的效果一样,但是Mixin可以继承
class PrintableMixin:
def print(self):
print(self.content,’Mixin’)
def printable(cls):
def _print(self):
print(self.content,’装饰器’)
cls.print = _print
return cls
class Docment():
def __init__(self,content):
self.content = content
class Word(Docment): pass
class Pdf(Docment): pass
class PrintableWord(PrintableMixin,Word): pass
print(PrintableWord.mro())
print(PrintableWord.__dict__)
pw = PrintableWord(‘test hhahha’)
pw.print()
class SuperPrintableMixin(PrintableMixin):
def print(self):
print(‘~’*20)
super().print()#通过集成服用,类似于装饰器
print(‘~’ * 20)
class SuperPrintablePdf(SuperPrintableMixin,Pdf): pass
print(SuperPrintablePdf.__dict__)
print(SuperPrintablePdf.mro())
spp = SuperPrintablePdf(‘super print pdf’)
spp.print()
——————————–
[<class ‘__main__.PrintableWord’>, <class ‘__main__.PrintableMixin’>, <class ‘__main__.Word’>, <class ‘__main__.Docment’>, <class ‘object’>]
{‘__doc__’: None, ‘__module__’: ‘__main__’}
test hhahha Mixin
{‘__doc__’: None, ‘__module__’: ‘__main__’}
[<class ‘__main__.SuperPrintablePdf’>, <class ‘__main__.SuperPrintableMixin’>, <class ‘__main__.PrintableMixin’>, <class ‘__main__.Pdf’>, <class ‘__main__.Docment’>, <class ‘object’>]
~~~~~~~~~~~~~~~~~~~~
super print pdf Mixin
~~~~~~~~~~~~~~~~~~~~
  • Mixin本质上就是多继承
  • Mixin体现的是一种组合模式
  • 在面向对象的设计中,一个复杂的类,往往需要很多功能,而这些功能有来自不同的类提供这就需要很多的类组合在一起
  • 从设计模式的角度来说,多组合少继承
  • Mixin使用原则
    • Mixin类中不应该出现init初始化方法
    • Mixin类通常不能独立工作,因为它是准备混入别的类中部分功能实现
    • Mixin类的祖先类应是Mixin类
  • 使用时Mixin类通常在继承列表的第一个位置
  • Mixin类和装饰器:
    • 这两种方式都可以使用,看个人选择
    • 如果还需要继承则使用Mixin
练习:
  1. shape基类,要求所有子类都必须提供面积的计算,子类有三角形、矩形、圆。圆类的数据可序列化
import math
import json
import msgpack
class Shape:
@property
def area(self):
raise NotImplemented(‘基类未实现’)
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
return math.sqrt(p*(p-self.a)*(p-self.b)*(p-self.c))
class Rectangle(Shape):
def __init__(self,width,height):
self.width = width
self.height = height
@property
def area(self):
return self.width * self.height
class Circle(Shape):
def __init__(self,radius):
self.d = radius * 2
@property
def area(self):
return math.pi * self.d * self.d * 0.25
class SerializableMixin:
def dumps(self,t=json):
if t == “json”:
return json.dumps(self.__dict__)
elif t == “msgpack”:
return msgpack.packb(self.__dict__)
else:
raise NotImplemented(“没有实现的序列化”)
class SerializableCircleMixin(SerializableMixin,Circle):
pass
scm = SerializableCircleMixin(4)
print(scm.area)
s = scm.dumps(‘msgpack’)
print(scm.__dict__)
print(s)
shapes = [Triangle(3,4,5),Rectangle(3,4),Circle(4)]
for s in shapes:
print(‘The area of {} = {}’.format(s.__class__.__name__,s.area))
—————————–
50.26548245743669
{‘d’: 8}#实例化对象的dict,也就是将他进行序列化
b’\x81\xa1d\x08′
The area of Triangle = 6.0
The area of Rectangle = 12
The area of Circle = 50.26548245743669
  1. 面向对象实现LinkedList链表,单向实现append、iternodes,双向列表实现append、insert、remove、iternodes
Alt text
#单向:
class SigleNode:
def __init__(self,val,next=None):
self.val = val
self.next = next
def __repr__(self):
return str(self.val)
def __str__(self):
return str(self.val)
class LinkList:
def __init__(self):
self.lst = []
self.head = None
self.tail = None
def add(self,val):
node = SigleNode(val)
curr = self.tail
if curr is None:
self.head = node
else:
curr.next = node
self.lst.append(node)
self.tail = node
def itemlst(self):
current = self.head
while current:
yield current
current = current.next
ll = SigleNode(5)
node = LinkList()
node.add(ll)
ll = SigleNode(7)
node.add(ll)
for x in node.itemlst():
print(x)

本文来自投稿,不代表Linux运维部落立场,如若转载,请注明出处:http://www.178linux.com/88407

(0)
上一篇 2017-11-15 16:00
下一篇 2017-11-15 21:52

相关推荐

  • Python第三周小结

    经过了三周的学习,我们已经基本完成了Python基础数据结构的学习,包括列表,字符串,元组,bytes, bytearray, set, 字典等。为了更好的理解和熟练使用这些基本的数据结构,我将它们各自的特点分别总结 并做成了表格,希望能够帮助我们更好的理解的同时,熟练掌握这些数据结构。    

    2018-04-10
  • Python第十二周学习总结

    Git的使用

    2018-05-27
  • 递归函数

    递归函数 def foo(b,b1=3):print(“foo1 called “,b,b1)def foo2(c):foo3(c)print(“foo2 called”,c)def foo3(d):print(“foo3 called”)def mian():print(“…

    2018-04-16
  • Python函数式编程指南(三):迭代器

    这一篇我们将讨论迭代器。迭代器并不是函数式编程特有的东西,但它仍然是函数式编程的一个重要的组成部分,或者说是一个重要的工具。 转载请注明原作者和原文地址:) 3. 迭代器 3.1. 迭代器(Iterator)概述 迭代器是访问集合内元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素都被访问一遍后结束。 迭代器不能回退,只能往前进行迭代。这并…

    Linux干货 2015-03-11