- 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 函数里名称和全局变量冲突 -- 改进
-
面向对象的三要素之一:继承
inheritance
-
在面向对象的世界中,从父类继承,就可以直接拥有父类的属性和方法,这样可以减少代码、多复用。
子类可以定义自己的属性和方法
-
继承:
class
子类(父类) 这种形式就是从父类继承,括号中写上继承的类的列表。继承可以让子类从父类获取特征(属性和方法)
-
父类:也称为 基类 超类
-
子类:也称为 派生类
-
定义:
class 子类名(基类1[, 基类2, ...]): 语句块
-
如果定义时,没有基类列表,等同于继承object
-
在Python3中, object类是所有对象的根基类
-
Python支持多继承,继承也可以多级
-
查看继承的特殊属性和方法有:
- 从父类继承,自己没有的,就可以到父类中找
- 私有的都是不可以访问的,但是本质上依然是改了名称放在这个属性所在类或实例的
__dict__
中,知道这个新名称就可以直接找到这个隐藏的变量,这是个黑魔法技巧,慎用 - 继承时,共有的,子类和实例都可以随意访问;私有成员被隐藏,子类和实例不可直接访问,但私有变量所在的类内的方法中可以访问这个私有变量
- 私有属性最好只在当前类中使用,修改名称时,在哪个类里修改为哪个类的名称
- 实例属性查找顺序:
实例的__dict__ ===> 类__dict__ === 如果有继承 ===> 父类__dict
- 继承关系中,同一种方法,在不同的类中有不同的表现方法,这就叫
Python的多态
- 自己的私有属性,就该自己的方法读取和修改,不要借助其他类的方法,即使是父类或者派生类的方法
- 作为好的习惯,如果父类定义了
__init__
方法,就该在子类的__init__
中调用它
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、实现多继承的语言,要解决二义性,深度优先或者广度优先
class ClassName(基类列表):
类体
Python
使用MRO(method resolution order 方法解析顺序)
解决基类搜索顺序问题C3
算法,在类被创建出来的时候,就计算出一个MRO
有序列表C3
算法解决多继承的二义性C3
算法,解决了继承的单调性,它阻止创建之前版本产生二义性的代码。求得的
MRO
本质是为了线性化,且确定了顺序- 单调性:假设有
A B C
三个类,C
的mro
是[C A B]
,那么C
的子类的mro
中,A B
的顺序一直就是单调的
-
用装饰器增强一个类,把功能给类附加上去,哪个类需要,就装饰哪个类
-
简单方便,在需要的地方动态增加,直接使用装饰器,可以为类灵活的增加功能
-
装饰器装饰类时,返回还是类
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 装饰器, , ]
Mixin
类
-
Mixin
本质上就是多继承实现的 -
Mixin
体现的是一种组合的设计模式 -
在面向对象的设计中,一个复杂的类,往往需要很多功能,而这些功能有来自不同的类提供,这就需要很多的类组合在一起
-
从设计模式的角度来说,多组合少继承
-
使用原则:
1Mixin
类中不应该显示的出现__init__
初始化方法2
Mixin
类通常不能独立工作,因为它是准备混入别的类中的部分功能实现3
Mixin
类的祖先类也应是Mixin
类 -
使用时,
Mixin
类通常在继承列表的第一个位置 -
Mixin
类和装饰器都可以使用,如果还需要继承就得使用Mixin
类的方式
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、函数名称和类方法名称注意事项
- 类方法名称如果使用全局变量的名称时,那是类属性,只能通过
类名.属性名
调用,并不和全局变量名称冲突
函数内,如果使用全局变量名称后,调用时,可能出现递归超限问题
# 通过此例可以看出,类属性方法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
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)