Python学习日记-3

191 阅读7分钟

面向对象的三大特征

封装(隐藏)

隐藏对象的属性和实现方法, 只对外提供必要的方法, 相当于将"细节封装起来", 只对外暴露"相关调用方法"

继承

继承可以让子类具有父类的特性, 提高了代码的重用性

从设计上是一种增量进化, 原有父类设计不变的情况下, 可以增量新的功能, 或者改进已有的算法

子类扩展父类(代码重用)

语法格式

Python支持多重继承, 一个子类可以继承多个父类, 继承的语法格式如下:

class 子类类名(父类1[,父类2...]):
    类体

如果在类定义中没有指定父类, 则默认父类是object, 也就是说, object是所有类的父类, 里面定义了一些所有类共有的默认实现, 比如: __new__()

关于构造函数:

  1. 子类不重写__init__, 实例化子类时, 会自动调用父类定义的__init__

  2. 子类重写了__init__时, 实例化子类时, 就不会调用父类已经定义的__init__

  3. 如果重写了__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安装各自的第三方包, 不同项目直接也不会有冲突

pycharm虚拟环境

vscode虚拟环境

虚拟环境管理工具: virtualenvwrapper