1.继承
1.1 什么是继承
让子类直接拥有父类的属性和方法的过程就是继承
父类 - 被继承者(又叫超类)
子类 - 继承者
1.2 怎么继承
class 类名(父类1,父类2,…):
类的说明文档
类的内容
注意: 默认情况下,定义的类继承自 object
class Person:
num = 61
def __init__(self):
print('Person中init')
self.name = '小明'
self.age = 18
self.gender = '男'
self.__sex = '女'
def eat(self):
print(f'Person:{self.name}在吃饭')
@classmethod
def show_num(cls):
print(f'人类的数量:{cls.num}')
@staticmethod
def func1():
print('人类破坏环境!')
让 Student(子类) 继承 Person(父类)
class Student(Person):
pass
继承的时候子类可以直接拥有父类所有的属性和方法
print(Student.num)
stu = Student()
print(stu.name, stu.age, stu.gender) # 小明 18 男
stu.eat() # Person:小明在吃饭
Student.show_num() # 人类的数量:61
Student.func1() # 人类破坏环境!
print(stu.__dict__) # {'name': '小明', 'age': 18, 'gender': '男', '_Person__sex': '女'}
1.3.在子类中添加内容
1)在子类中添加类属性和方法
类属性和方法的添加不会因为继承而收到任何影响
- 添加对象属性
对象属性是怎么被继承:继承的时候因为init方法被继承,间接继承了对象属性
在子类的__init__方法中通过 super()去调用父类的__init__方法
类中的方法的调用过程(重要):
通过类或者对象在调用方法的时候,会先看当前类中有没有这个方法,如果有就直接调用自己类中的方法;没有就看父类中有没有定义这个方法,如果父类定义了就调用父类的;父类没有定义,就看父类的父类中有没有定义…以此类推,如果 object 中没有定义才会报错!
例1:
class Teacher(Person):
title = '老师'
def __init__(self):
# 调用父类的init方法
super().__init__()
self.school = '千锋'
self.subject = 'Python'
self.tea_id = '001'
def attend_class(self):
print('老师上课')
def eat(self):
super().eat()
print(f'老师在吃饭')
print(Teacher.num, Teacher.title)
t1 = Teacher()
t1.attend_class()
print(t1.school, t1.subject, t1.tea_id)
print(t1.name)
t1.eat()
例2:
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self, food):
print(self.name + '正在吃' + food)
class Student(Person):
# 重写父类方法的时候,先把父类方法的行为调用一遍
# 相当于在父类原有方法上进行功能的扩充
def __init__(self, name, age, stu_id):
# self.name = name
# self.age = age
# 子类调用父类的方法:
# 1. 方式1:使用父类名.方法名(参数)
# Person.__init__(self, name, age) # 调用Person类里的__init__ 方法
# 2. 方式2:使用super
super(Student, self).__init__(name, age)
self.stu_id = stu_id
def study(self):
print(self.name + '正在学习')
# 子类定义了和父类同名的方法(重写了父类的方法)
def eat(self, food):
print('学生' + self.name + '正在吃' + food)
s1 = Student('佳佳', 18, '002')
s1.eat('草莓') # 根据__mro__规则,先调用自己类的方法
s1.study()
s2 = Student('婷婷', 16, '001')
s2.eat('酸辣土豆丝')
# 子类重写父类方法:
# 1. 子类完全重写父类的方法,和父类的方法没有关系,比如这里的eat()方法
# 2. 子类在重写父类方法之前,先调用一下父类的方法,比如这里的init方法。 super()
2 多继承
多继承的使用注意事项:
如果不同的父类中存在同名的方法,子类对象在调用方法时,会调用哪个父类的方法?
Python中的MRO
Python中针对类提供了一个内置属性__mro__可以用来查看方法的搜索顺序。
MRO 是method resolution order的简称,主要用于在多继承时判断方法属性的调用顺序
print(类名.__mro__)
class Animal:
num = 10
def __init__(self, age, gender):
self.age = age
self.gender = gender
def eat(self):
print('动物需要吃东西')
class Fly:
name = '飞行器'
def __init__(self, height, time):
self.height = height
self.time = time
def stop(self):
self.height = 0
print('停止飞行')
class Bird(Animal, Fly):
pass
b1 = Bird(2, '雌')
两个父类的类属性都可以继承
print(Bird.num) # 10
print(Bird.name) # 飞行器
对象属性只会继承第一个父类的
print(b1.age, b1.gender)
# print(b1.height, b1.time) # AttributeError: 'Bird' object has no attribute 'height'
两个父类的不同方法都可以继承
b1.eat()
b1.stop()
面试题:
class A:
def show(self):
print('A')
class B(A):
def show(self):
super(B, self).show()
print('B')
class C(A):
def show(self):
super().show()
print('C')
class D(B, C):
def show(self):
super().show()
print('D')
print('==============d============')
# mro:DBCA
d = D()
d.show() # A C B D
#
# print(D.__mro__)
print('===============b===============')
b = B()
b.show() # A B
面试题扩展
class A:
def show(self):
print('A')
class B(A):
def show(self):
super().show()
print('B')
class C(A):
def show(self):
super().show()
print('C')
class D(A):
def show(self):
super().show()
print('D')
class E(B, C):
def show(self):
super().show()
print('E')
class F(C, D):
def show(self):
super().show()
print('F')
class G(F, E):
def show(self):
super().show()
print('G')
print('=========G========')
# G 的mro: GFEBCDA
# G
g = G()
g.show() # 顺序 A D C B E F G
print('==================')
# F 的mro: FCDA
f = F()
f.show() # 顺序 A D C F
super的用法
super(类, 对象) - 获取指定类的父类(对象必须是类的对象; 类默认指向当前类,对象默认是当前类的对象)
class A:
def show(self):
print('A')
pass
class B(A):
def show(self):
print('B')
class B2:
def show(self):
print('B2')
class C(B, B2):
def show(self):
# super().show() # super(C, self).show()
# super(C, self).show()
super(B, B()).show() # 调用B的父类的show方法
print('C')
print('============C:=============')
c = C()
c.show() # A C
3.运算符重载
from copy import copy
3.1 运算符重载
python中使用每一个运算符其本质都是在调用运算符对应的方法(每个运算符都会对应一个固定的方法)。
某种类型的数据支不支持某个运算符,就看这个数据对应的类型中有没有实现运算符对应的方法
10 + 20 # 10.__add__(20)
'abc' + '123' # 'abc'.__add__('123')
100 - 10 # 100.__sub__(10)
dict
# {'a': 10} + {'b': 20}
class Person:
def __init__(self, name='', age=0):
self.name = name
self.age = age
# +
def __add__(self, other):
# self + other
return self.age + other.age
# *
def __mul__(self, other):
list1 = []
for _ in range(other):
list1.append(copy(self))
return list1
# <
# 注意:大于符号和小于符号实现其中一个另外一个也可以用
def __lt__(self, other):
return self.age < other.age
def __repr__(self):
return f'<{str(self.__dict__)[1:-1]}>'
p1 = Person('小明', age=18)
p2 = Person(age=30)
print(p1 == p2) # False
print(p1 > p2) # False
print(p1 + p2) # p1.__add__(p2) 48
# 10+20 -> 10.__add__(20)
# 10 * 2
print(p1 * 3) # [<'name': '小明', 'age': 18>, <'name': '小明', 'age': 18>, <'name': '小明', 'age': 18>]
persons = [Person('小花', 20), Person('Tom', 18), Person('张三', 28), Person('李四', 15)]
persons.sort()
print(persons) # [<'name': '李四', 'age': 15>, <'name': 'Tom', 'age': 18>, <'name': '小花', 'age': 20>, <'name': '张三', 'age': 28>]
4.单例模式
from copy import copy
4.1 单例类
我们日常使用的电脑上都有一个回收站,在整个操作系统中,回收站只能有一个实例,整个系统都使用这个唯一的实例,而且回收站自行提供自己的实例。因此回收站是单例模式的应用。
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,单例模式是一种对象创建型模式。
class Person:
__obj = None
def __new__(cls, *args, **kwargs):
# print('new')
# new_obj = super().__new__(cls)
# print('new:', new_obj)
# return new_obj
if not cls.__obj:
cls.__obj = super().__new__(cls)
return cls.__obj
def __init__(self):
print('init:', self)
p1 = Person()
p2 = Person()
p3 = copy(p1)
print(p1 is p2)
print(p1 is p3)
def Person(*args, **kwargs):
对象 = Person.__new__()
对象.__init__(*args, **kwargs)
return 对象
# init: <__main__.Person object at 0x0000000001E6E208>
# init: <__main__.Person object at 0x0000000001E6E208>
# True
# True
__new__方法的使用
总结:
1.__new__至少要有一个参数cls,代表要实例化的类,此参数在实例化时由Python解释器自动提供。用内创建内存空间
2.__new__必须要有返回值,返回实例化出来的实例,这点在自己实现__new__时要特别注意,可以return父类__new__出来的实例,或者直接是object的__new__出来的实例
3.__init__有一个参数self,就是这个__new__返回的实例,__init__在__new__的基础上可以完成一些其它初始化的动作,__init__不需要返回值
class Person(object):
def __new__(cls, *args, **kwargs):
print('__new__方法被调用了!!!')
instance = object.__new__(cls) # 创建了__new__就必须根据类对象申请内存空间,创建实例对象
return instance # 返回内存值,没有这两步就创建不了对象
def __init__(self, name, age):
print('__init__ 方法被调用了!!!')
self.name = name
self.age = age
# 1. 调用 __new__ 方法用来创建内存空间。
# 2. 调用 __init__ 方法,并把 __init__ 里的self指向创建好的内存空间
# 3. 在 __init__ 方法里,给申请好的内存空间填充数据
# 4. p1 指向创建好的内存空间
p1 = Person('明明', 18)
print(p1)