面向对象的三大特征
封装(隐藏)
隐藏对象的属性和实现方法, 只对外提供必要的方法, 相当于将"细节封装起来", 只对外暴露"相关调用方法"
继承
继承可以让子类具有父类的特性, 提高了代码的重用性
从设计上是一种增量进化, 原有父类设计不变的情况下, 可以增量新的功能, 或者改进已有的算法
子类扩展父类(代码重用)
语法格式
Python支持多重继承, 一个子类可以继承多个父类, 继承的语法格式如下:
class 子类类名(父类1[,父类2...]):
类体
如果在类定义中没有指定父类, 则默认父类是object, 也就是说, object是所有类的父类, 里面定义了一些所有类共有的默认实现, 比如: __new__()
关于构造函数:
-
子类不重写__init__, 实例化子类时, 会自动调用父类定义的__init__
-
子类重写了__init__时, 实例化子类时, 就不会调用父类已经定义的__init__
-
如果重写了__init__时, 又要继承父类的构造函数, 可以使用super关键字, 也可以使用如下格式进行调用:
父类名.__init__(self.参数列表)
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
class Student(Person):
def __init__(self, name, age, score):
# Person.__init__(self, name, age)
super(Student, self).__init__(name, age)
self.score = score
类方法的继承和重写
成员继承: 子类继承了父类除构造方法之外的所有成员(私有属性, 私有方法也被继承)
方法重写: 子类可以重新定义父类中的方法, 这样就会覆盖父类的方法, 也成为"重写"
查看类的继承层次结构
通过类的方法mro()或者类的属性__mro__可以输出这个类的继承层次结构
MRO方法解析顺序
Python支持多继承, 如果父类中有相同名字的方法, 在子类没有指定父类名时, 解释器将"从左到右"按顺序搜索
MRO(Method Resolution Order): 方法解析顺序, 我们可以通过mro()方法获得类的层次结构, 方法解析顺序就是按照这个类的层次结构寻找的
Object根类
查看模块结构: alt + 7 或者 dir()函数
方法实际也是属性, 只不过类型为method
重写__str__()方法
object()有一个__str__()方法, 用于返回"对象的描述", 内置函数str(对象), 调用的就是__str__()
__str__()经常用于print()方法, 帮助我们查看对象的信息. __str__()可以重写
class Person:
def __init__(self, name, age):
self.name = name
self.__age = age
def __str__(self):
"""将对象转化为一个字符串描述, 一般用于print方法"""
print("重写了__str__方法")
return f"名字是{self.name}, 年龄是{self.__age}"
p = Person("兔子", 18)
print(p)
s = str(p)
多重继承
Python支持多重继承, 一个子类可以有多个"直接父类", 这样, 就具备了"多个父类"的特定, eg. class C(A, B)
但是这样会把类的整体层次搞的异常复杂, 尽量避免使用
super()获得父类定义
在子类中,如果想要获得父类的方法时, 可以通过super()函数
super()代表父类的定义, 不是父类对象
调用父类的构造方法格式: super(子类名称, self).init(参数列表)
多态
多态是指同一个方法调用, 由于对象不同会产生不同的行为
多态是方法的多态, 属性没有多态
多态的存在有两个必要条件: 继承, 方法重写
class Animal:
def shout(self):
print("动物叫了一声")
class Dog(Animal):
def shout(self):
print("小狗汪汪叫")
class Cat(Animal):
def shout(self):
print("小猫喵喵叫")
def animal_shout(a):
a.shout()
animal_shout(Dog())
animal_shout(Cat())
特殊方法和运算符重载
Python的运算符实际上是通过调用对象的特殊方法实现的
常见的特殊方法统计如下:
| 方法 | 说明 | 例子 |
|---|---|---|
| __init__ | 构造方法 | 对象初始化 |
| __del__ | 析构方法 | 对象回收 |
| __repr__, __str__ | 打印, 转换 | print(a) |
| __call__ | 函数调用 | a() |
| __getattr__ | 点号运算 | a.xxx |
| __setattr__ | 属性赋值 | a.xxx=value |
| __getitem__ | 索引运算 | a[key] |
| __setitem__ | 索引赋值 | a[key]=value |
| __len__ | 长度 | len(a) |
运算符的特殊方法如下:
| 运算符 | 特殊方法 | 说明 |
|---|---|---|
| + | __add__ | 加法 |
| - | __sub__ | 减法 |
| <, <=, == | __lt__, __le__, __eq__ | 比较运算符 |
| >, >=, != | __gt__, __ge__, __ne__ | 比较运算符 |
| |, ^, & | __or__, __xor__, __and__ | 或, 异或, 与 |
| <<, >> | __lshirt__, __rshirt__ | 左移, 右移 |
| *, /, %, // | __mul__, __truediv__, __mod__, __floordiv__ | 乘法, 浮点除, 取余, 整数除 |
| ** | __pow__ | 指数运算 |
特殊属性
Python对象中包含了很多双下划线开始和结束的属性, 这里我们列出常见的特殊属性:
| 特殊属性 | 含义 |
|---|---|
| obj.__dict__ | 对象的属性字典 |
| obj.__class__ | 对象所属的类 |
| obj.__bases__ | 类的父类元组(多继承) |
| obj.__base__ | 类的父类 |
| obj.__mro__ | 类层次结构 |
| obj.__subclasses__ | 子类列表 |
浅拷贝和深拷贝
浅拷贝
Python拷贝一般都是浅拷贝, 拷贝时, 对象包含的子对象内容不拷贝, 因此, 源对象和拷贝对象会引用同一个子对象
深拷贝
使用copy模块的deepcopy函数, 递归拷贝对象中包含的子对象, 源对象和拷贝对象所有的子对象也不同
import copy
class MobilePhone:
def __init__(self, cpu):
self.cpu = cpu
class CPU:
pass
c = CPU()
m1 = MobilePhone(c)
m2 = copy.copy(m1)
m3 = copy.deepcopy(m1)
print(id(m1), id(m2), id(m3))
print(id(m1.cpu), id(m2.cpu), id(m3.cpu))
继承和组合
除了继承, 组合也能实现代码的复用, 组合的核心是将父类对象作为子类的属性
is-a关系: 我们可以使用继承, 从而实现子类拥有父类的方法和属性, is-a关系是指XX是YY的意思, 例如: 狗是动物, 因此狗就应该继承动物类
has-a关系: 我们可以使用组合, 从而实现一个类拥有另一个类的方法和属性, has-a关系是指XX拥有YY的意思, 例如: 手机拥有CPU, 因此CPU是手机的组成之一
class CPU:
def calculate(self):
print("正在计算中...")
class Screen:
def show(self):
print("正在显示中...")
class MobilePhone:
def __init__(self, cpu, screen):
self.cpu = cpu
self.screen = screen
c = CPU()
s = Screen()
m = MobilePhone(c, s)
m.cpu.calculate()
m.screen.show()
设计模式
设计模式是面向对象特有的内容, 是我们面临某一类问题时固定的做法, 设计模式有很多种, 比较流行的是: GOF(Goup Of Four) 23种设计模式, 其中对于初学者, 最常用的设计模式: 工厂模式和单例模式
工厂模式实现
工厂模式实现了创建者和调用者的分离, 使用专门的工厂类将选择实现类, 创建对象进行统一的管理和控制
class Benz: pass
class BMW: pass
class BYD: pass
class CarFactory:
def CreateCar(self, brand):
if brand == "奔驰":
return Benz()
elif brand == "宝马":
return BMW()
elif brand == "比亚迪":
return BYD()
else:
return "品牌未知, 无法创建"
factory = CarFactory()
car1 = factory.CreateCar("奔驰")
单例模式实现
单例模式(Singleton Pattern)的核心作用是确保一个类只有一个实例, 并且提供一个访问该实例的全局访问点
单例模式只生成一个实例对象, 减少了对系统资源的开销, 当一个对象的产生需要比较多的资源, 如读取配置文件, 产生其他依赖对象时, 可以产生一个单例对象, 然后永久驻留内存中, 从而极大降低开销
单例模式有很多实现方式, 其中有重写__new__()方法
class MySingleton:
__obj = None
__init_flag = True
def __new__(cls, *args, **kwargs):
if cls.__obj == None:
cls.__obj = object.__new__(cls)
return cls.__obj
def __init__(self):
if MySingleton.__init_flag:
print("初始化对象...")
MySingleton.__init_flag = False
a = MySingleton()
print(a)
b = MySingleton()
print(b)
工厂和单例模式结合
class Benz: pass
class BMW: pass
class BYD: pass
class CarFactory:
__obj = None
__init_flag = True
def __new__(cls, *args, **kwargs):
if cls.__obj == None:
cls.__obj = object.__new__(cls)
return cls.__obj
def __init__(self):
if CarFactory.__init_flag:
print("初始化对象...")
CarFactory.__init_flag = False
def CreateCar(self, brand):
if brand == "奔驰":
return Benz()
elif brand == "宝马":
return BMW()
elif brand == "比亚迪":
return BYD()
else:
return "品牌未知, 无法创建"
factory = CarFactory()
car1 = factory.CreateCar("奔驰")
虚拟环境
虚拟环境就是虚拟出来的一个隔离的python环境, 每个项目都可以有自己的虚拟环境, 用pip安装各自的第三方包, 不同项目直接也不会有冲突