Python Class 类的继承与多继承

Python Class 类的继承与多继承,第1张

Python Class 类的继承与多继承
  • 1、继承概念
  • 2、继承中的访问控制
  • 3、示例
    • 3.1 示例 1
    • 3.2 示例 2
    • 3.3 示例 3
    • 3.4 示例 4
    • 3.5 示例 5
    • 3.6 示例 6
    • 3.7 示例 7
    • 3.8 示例 8
    • 3.9 示例 9
  • 4、多继承
    • 4.1 概念介绍
    • 4.2 多继承语法
    • 4.3 类装饰器
    • 4.4 `Mixin` 类
    • 4.5 `Mixin` 示例
  • 5、类抽象方法
  • 6、函数名称和类方法名称注意事项
    • 6.1 类方法名称和全局变量冲突
    • 6.2 函数里名称和全局变量冲突
    • 6.3 函数里名称和全局变量冲突 -- 改进

1、继承概念
  • 面向对象的三要素之一:继承 inheritance

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


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

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


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

  • 父类:也称为 基类 超类

  • 子类:也称为 派生类

  • 定义:

    class 子类名(基类1[, 基类2, ...]):
        语句块
    
  • 如果定义时,没有基类列表,等同于继承object

  • 在Python3中, object类是所有对象的根基类

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

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

2、继承中的访问控制
  • 从父类继承,自己没有的,就可以到父类中找
  • 私有的都是不可以访问的,但是本质上依然是改了名称放在这个属性所在类或实例的__dict__中,知道这个新名称就可以直接找到这个隐藏的变量,这是个黑魔法技巧,慎用
  • 继承时,共有的,子类和实例都可以随意访问;私有成员被隐藏,子类和实例不可直接访问,但私有变量所在的类内的方法中可以访问这个私有变量
  • 私有属性最好只在当前类中使用,修改名称时,在哪个类里修改为哪个类的名称
  • 实例属性查找顺序:实例的__dict__ ===> 类__dict__ === 如果有继承 ===> 父类__dict
  • 继承关系中,同一种方法,在不同的类中有不同的表现方法,这就叫Python的多态
  • 自己的私有属性,就该自己的方法读取和修改,不要借助其他类的方法,即使是父类或者派生类的方法
  • 作为好的习惯,如果父类定义了__init__方法,就该在子类的__init__中调用它
3、示例 3.1 示例 1
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()  # Animal shouts.
a.name  # 'Monster'

class Dog(Animal):
    pass

dog = Dog('Maltese')
dog.shout()  # Dog shouts.
dog.name  # 'Maltese'

class Cat(Animal):
    pass

cat = Cat('Garfield')
cat.shout()  # Cat shouts.
cat.name  # 'Garfield'
Cat.__base__  # __main__.Animal   
Cat.__bases__  # (__main__.Animal,)
Cat.__mro__  # (__main__.Cat, __main__.Animal, object)
Cat.mro()  # [__main__.Cat, __main__.Animal, object]
Animal.__subclasses__()  # [__main__.Dog, __main__.Cat]
3.2 示例 2

看懂如下示例,继承就可以完全get了。


class Animal:
    __COUNT = 100  # _Animal__COUNT
    HEIGHT = 0
    
    def __init__(self, age, weight, height):
        self.__COUNT += 1  # slef._Animal__COUNT
        self.age = age
        self.__weight = weight  # self._Animal__COUNT
        self.HEIGHT = height
    
    def eat(self):
        print("{} eat".format(self.__class__.__name__))
        
    def __getweight(self):
        print(self.__weight)  # self._Animal__COUNT
        
    @classmethod
    def showcount1(cls):
        print(cls)
        print(cls.__dict__)
        print(cls.__COUNT)  # cls._Animal__COUNT
    
    @classmethod
    def __showcount2(cls):
        print(cls.__COUNT)  # cls._Animal__COUNT
        
    def showcount3(self):
        print(self.__COUNT)  # self._Animal__COUNT
        

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

c = Cat(3, 5, 15)
print(c.__dict__)  # {'_Animal__COUNT': 101, 'age': 3, '_Animal__weight': 5, 'HEIGHT': 15}
print(c.HEIGHT)  # 15
# print(c.__COUNT)  # 'Cat' object has no attribute '__COUNT'
# c.__getweight()  #  'Cat' object has no attribute '__getweight'
c._Animal__getweight()  # 5  【注意,隐藏属性改名的只和当前类有关系,设置私有属性的当前类,在谁里面改谁的名】
c.showcount1()  # 自己没有的,会和父类去要
    # 
    # {'__module__': '__main__', 'NAME': 'CAT', '_Cat__COUNT': 200, '__doc__': None}
    # 100 

# c.__showcount2()  # 'Cat' object has no attribute '__showcount2'
c._Animal__showcount2()  # 100
c.showcount3()  # 101
print(Cat.__dict__)  # {'__module__': '__main__', 'NAME': 'CAT', '_Cat__COUNT': 200, '__doc__': None}
print(c._Cat__COUNT)  # 200
print(c._Animal__COUNT)  # 101
print(c.NAME)  # CAT
print(Animal.__dict__)  # {'__module__': '__main__', '_Animal__COUNT': 100, 'HEIGHT': 0, '__init__': , 'eat': , '_Animal__getweight': , 'showcount1': , '_Animal__showcount2': , 'showcount3': , '__dict__': , '__weakref__': , '__doc__': None}
print(c.__class__.mro())  # [, , ]
3.3 示例 3
class A:
    X = 100
    @classmethod
    def showx(cls):
        print(cls)  # 当前类
        print(cls.X)

class B(A):
    X = 200

B().showx()
# 
# 200
3.4 示例 4
class Animal:
    def shout(self):
        print('Animal Shouts')

class Cat(Animal):
    def shout(self):
        print('Miao')

a = Animal()
a.shout()  # Animal Shouts
c = Cat()
c.shout()  # Miao

a.__dict__  # {}
Animal.__dict__['shout']  # 
c.__dict__  # {}
Cat.__dict__['shout']  # 
3.5 示例 5
Init signature: super(self, /, *args, **kwargs)
Docstring:     
super() -> same as super(__class__, <first argument>)
super(type) -> unbound super object
super(type, obj) -> bound super object; requires isinstance(obj, type)
super(type, type2) -> bound super object; requires issubclass(type2, type)
class Animal:
    def shout(self):
        print('Animal Shouts')

class Cat(Animal):
    # 覆盖了父类方法
    def shout(self):
        print('Miao')
    # 覆盖了自身的方法,显式调用了父类的方法和属性
    # super() 可以调用父类的方法和属性
    def shout(self):
        print(super())
        print(super(Cat, self))
        print(super(self.__class__, self))
        
        super().shout()  # 此处并不需要实例self作为参数注入,是因为super()里已经有类和实例了,会自动注入和绑定
        super(Cat, self).shout()
        self.__class__.__base__.shout(self)  # 不推荐
        
a = Animal()
a.shout()  # Animal Shouts     
c = Cat()
c.shout() 
# , >
# , >
# , >
# Animal Shouts
# Animal Shouts
# Animal Shouts
print(a.__dict__)  # {}
print(c.__dict__)  # {}
print(Animal.__dict__)
# {'__module__': '__main__', 'shout': , '__dict__': , '__weakref__': , '__doc__': None}
print(Cat.__dict__)
# {'__module__': '__main__', 'shout': , '__doc__': None}
3.6 示例 6
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()  # class_method_cat
c.static_method()  # static_method_cat
print(Cat.__dict__)
# {'__module__': '__main__', 'class_method': , 'static_method': , '__doc__': None}
print(Animal.__dict__)
# {'__module__': '__main__', 'class_method': , 'static_method': , '__dict__': , '__weakref__': , '__doc__': None}
Cat.static_method()  # static_method_cat
Animal.static_method()  # static_method_animal
Cat.class_method()  # class_method_cat
Animal.class_method()  # class_method_animal
3.7 示例 7
# 继承时,使用初始化
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.b + self.c)
f = B(200, 300)
# 注意实例字体和类字典的区别
print(f.__dict__)  # {'b': 200, 'c': 300}
print(f.__class__.__bases__)  # (,)
print(B.__dict__)  
# {'__module__':'__main__', '__init__': , 'printv': , '__doc__': None}
f.printv()  # 200 500
3.8 示例 8
class Animal:
    def __init__(self, age):
        print('init in Animal')
        self.__age = age  # self._Animal__age
        
    def show(self):
        print(self.__age)  # self._Animal__age
        
class Cat(Animal):
    def __init__(self, age, weight):
        super().__init__(age)
        print('init in Cat')
        self.__age = age + 1  # _Cat__age__
        self.__weight = weight  # _Cat__weight__
    
miao = Cat(10, 5)
# init in Animal
# init in Cat
miao.show()
# 10
print(miao.__dict__)
# {'_Animal__age': 10, '_Cat__age': 11, '_Cat__weight': 5}
3.9 示例 9
class Animal:
    def __init__(self, age):
        print('init in Animal')
        self.__age = age  # self._Animal__age
        
    def show(self):
        print(self.__age)  # self._Animal__age
        
class Cat(Animal):
    def __init__(self, age, weight):
        print('init in Cat')
        self.__age = age + 1  # _Cat__age__
        self.__weight = weight  # _Cat__weight__
        super().__init__(age)
            
miao = Cat(10, 5)
# init in Animal
# init in Cat
miao.show()
# 10
print(miao.__dict__)
# {'_Animal__age': 10, '_Cat__age': 11, '_Cat__weight': 5}
4、多继承 4.1 概念介绍
  • 多继承
    1、COP原则:多用继承,少修改

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

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

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

  • 多继承弊端
    1、多继承很好的模拟了世界,因为事物很少是单一继承,但是舍弃简单,必然引入复杂性,带来了冲突

    2、多继承的实现会导致编译器设计的复杂性增加,所以有些高级编程语言舍弃了类的多继承

    3、实现多继承的语言,要解决二义性,深度优先或者广度优先

4.2 多继承语法
class ClassName(基类列表):
    类体
  • Python使用MRO(method resolution order 方法解析顺序)解决基类搜索顺序问题
  • C3算法,在类被创建出来的时候,就计算出一个MRO有序列表
  • C3算法解决多继承的二义性
  • C3算法,解决了继承的单调性,它阻止创建之前版本产生二义性的代码。


    求得的MRO本质是为了线性化,且确定了顺序

  • 单调性:假设有A B C三个类,Cmro[C A B],那么C的子类的mro中,A B的顺序一直就是单调的
4.3 类装饰器
  • 用装饰器增强一个类,把功能给类附加上去,哪个类需要,就装饰哪个类

  • 简单方便,在需要的地方动态增加,直接使用装饰器,可以为类灵活的增加功能

  • 装饰器装饰类时,返回还是类

    def printable(cls):
        def _print(self):
            print(self.content, '装饰器')
        cls.print = _print
        return cls
    
    class Document:
        def __init__(self, content):
            self.content = content
    class Word(Document):pass
    class Pdf(Document):pass
    
    @printable
    class PrintableWord(Word):pass
    
    print(PrintableWord.__dict__)
    # {'__module__': '__main__', '__doc__': None, 'print': ._print at 0x0000017DB2C47310>}
    print(PrintableWord.mro())
    # [, , , ]
    pw = PrintableWord('Test String')
    pw.print()
    # Test String 装饰器
    
4.4 Mixin
  • Mixin本质上就是多继承实现的

  • Mixin体现的是一种组合的设计模式

  • 在面向对象的设计中,一个复杂的类,往往需要很多功能,而这些功能有来自不同的类提供,这就需要很多的类组合在一起

  • 从设计模式的角度来说,多组合少继承

  • 使用原则:
    1 Mixin 类中不应该显示的出现__init__初始化方法

    2 Mixin 类通常不能独立工作,因为它是准备混入别的类中的部分功能实现

    3 Mixin 类的祖先类也应是Mixin

  • 使用时,Mixin 类通常在继承列表的第一个位置

  • Mixin类和装饰器都可以使用,如果还需要继承就得使用Mixin类的方式

4.5 Mixin 示例
class Document:
    def __init__(self, content):
        self.content = content

class Word(Document):pass
class Pdf(Document):pass

class PrintableMixin:
    def print(self):
        print(self.content, ' + Mixin')

class PrintableWord(PrintableMixin, Word):pass
print(PrintableWord.__dict__)  
# {'__module__': '__main__', '__doc__': None}

print(PrintableWord.mro())
# [, , , , ]

pw = PrintableWord('Word Test String')
pw.print()
# Word Test String  + Mixin

class SuperPrintableMixin(PrintableMixin):
    def print(self):
        print('=== Before SuperPrintableMixin ===')
        super().print()
        print('=== After SuperPrintableMixin ===')
        
class SuperPrintablePdf(SuperPrintableMixin, Pdf):pass

print(SuperPrintablePdf.__dict__)  
# {'__module__': '__main__', '__doc__': None}

print(SuperPrintablePdf.mro())
# [, , , , , ]

spp = SuperPrintablePdf('Pdf Test String Super Print + SuperPrintable Mixin')
spp.print()
# === Before SuperPrintableMixin ===
# Pdf Test String Super Print + SuperPrintable Mixin  + Mixin
# === After SuperPrintableMixin ===
5、类抽象方法

抽象方法,子类需要完善,如果子类不完善,解释器会提示。


class Document:
    def __init__(self, content):
        self.content = content
        
    def print(self):  # 抽象方法
        raise NotImplementedError("This function havn't implemented.")

        
class Pdf(Document):
    def print(self):  # 覆盖掉基类的抽象方法
        print(self.content)

        
p1 = Pdf('test string pdf')
p1.print()

# test string pdf

6、函数名称和类方法名称注意事项
  • 类方法名称如果使用全局变量的名称时,那是类属性,只能通过类名.属性名调用,并不和全局变量名称冲突
    函数内,如果使用全局变量名称后,调用时,可能出现递归超限问题
6.1 类方法名称和全局变量冲突
# 通过此例可以看出,类属性方法print和全局print并不冲突

class Document:
    def __init__(self, content):
        self.content = content
    
    def print(self):
        raise NotImplementedError('Not Implemented')
        

class Word(Document):pass

class Printable:
    def print(self):
        print(self.content)
        
class PrintableWordMixin(Printable, Word):
    pass

instance1 = PrintableWordMixin('word test string')
PrintableWordMixin.mro()
# [__main__.PrintableWordMixin, __main__.Printable, __main__.Word, __main__.Document, object]
instance1.print()
# word test string
6.2 函数里名称和全局变量冲突
class Document:
    def __init__(self, content):
        self.content = content
    
    def print(self):
        raise NotImplementedError('Not Implemented')

class Word(Document):pass
        
def printable(cls):
    def print(self):
        print(self.content)
    cls.print = print
    return cls
        
@printable  # printable(PrintableWord)
class PrintableWord(Word):pass

instance1 = PrintableWord('word test string')
instance1.print()
AttributeError                            Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_29088/4095735764.py in <module>
     18 
     19 instance1 = PrintableWord('word test string')
---> 20 instance1.print()

~\AppData\Local\Temp/ipykernel_29088/4095735764.py in print(self)
... ...
AttributeError: 'str' object has no attribute 'content'
6.3 函数里名称和全局变量冲突 – 改进
class Document:
    def __init__(self, content):
        self.content = content
    
    def print(self):
        raise NotImplementedError('Not Implemented')

class Word(Document):pass
        
def printable(cls):
    def _print(self):  # 避免和全局变量print冲突
        print(self.content)
    cls.print = _print  # 动态添加类属性
    return cls
        
@printable  # printable(PrintableWord)
class PrintableWord(Word):pass

instance1 = PrintableWord('word test string')
instance1.print()
# word test string

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/571615.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-09
下一篇 2022-04-09

发表评论

登录后才能评论

评论列表(0条)

保存