python面向对象复习笔记

python面向对象复习笔记

    • python语言的“万物皆对象”
    • 面向对象的三个特征
    • 类的基本构建方法
    • python类的封装
      • 多看一眼:私有属性
      • 类的保留属性/方法
    • python类的继承
      • 类的属性重载
      • 类的多继承
      • python类的运算
    • python类的多态
      • 引用Reference:对象的指针
        • python内部机制对引用的处理:
        • 导致引用+1的情况
        • 导致引用-1的情况
        • 引用Reference:对象的指针
        • 浅拷贝和深拷贝
        • 类的方法引用
    • python类的高级话题
      • 命名空间的理解
      • 类的特性装饰器
      • 自定义的异常类型
      • 类的名称修饰
      • python的最小空类

b站学习视频:https://www.bilibili.com/video/BV1M54y1d7UB?p=1
北理嵩天老师python课程笔记

python语言的“万物皆对象”

  • python语言中所有数据类型都是对象,函数是对象,模块是对象
  • python所有类都继承于最基础类object
  • python语言中数据类型的操作功能都是类方法的体现

面向对象的三个特征

  • 封装:属性和方法的抽象,用数据和操作数据的方法来形成对象逻辑
  • 继承:代码复用的高级抽象,用对象之间的继承关系来形成代码复用
  • 多态:方法灵活性的抽象,让对象的操作更加灵活,更多复用代码

封装:对类的变量、函数进行定义、隔离以及保护,形成一个类对外可操作变量的函数的接口。多态是OOP的一个传统概念,Python天然支持多态,不需要特殊语法。

类对象 VS. 实例对象
类对象:Class Object,维护每个python类基本信息的数据结构
实例对象:Instance Object,Python类实例后产生的对象,简称:对象
这是一组概念,类对象全局只有一个,实例对象可以生成多个。

类的基本构建方法

类的名字:建议采用大写单词的组合
类描述:在类定义后首行,以独立字符串形式定义
定义后通过<类名>._doc_属性来访问

  • 类定义完成后,默认生成一个类对象
  • 每个类唯一对应一个类对象,用于存储整个类的基本信息
  • 类对象是type类的实例,表达为type类型。

**注意:类对象中直接包含的语句会被执行,因此,一般不在类定义中直接包含语句。
**

类的构造函数:

  • 类的构造函数用于从类创建实例对象的过程
  • 类的构造函数为实例对象创建提供了参数输入方式
  • 类的构造函数为实例属性的定义和赋值提供了支持

python使用预定义的__init__()作为构造函数
使用说明:
参数:第一个参数约定是self,表示类实例自身,其他参数是实例参数
函数名:python解释器内部定义,由双下划线开始和技术
返回值:构造函数没有返回值,或返回None,否则产生TypeError异常。

方法是类内部定义的函数

  • 实例方法:实例对象的方法,由各实例对象独享,最常用的形式
  • 类方法:类对象的方法,由所有实例对象共享
  • 自由方法:类中一个普通函数,由类所在命名空间管理,类对象独享
  • 静态方法:类中一个普通函数,由类对象和实例对象共享
  • 保留方法:由双下划线开始和结束的方法,保留使用

类方法:

  • 类方法至少包含一个参数,表示类对象,建议使用cls
  • @classmethod是装饰器,类方法定义所必须
  • 类方法只能操作类属性和其他类方法,不能操作实例属性和实例方法,但实例对象可以调用类方法。

自由方法:

  • 自由方法不需要self和cls这类参数,可以没有参数
  • 自由方法只能操作类属性和类方法,不能操作实例属性和实例方法
  • 自用方法的使用只能使用<类名>

静态方法:

  • 静态方法可以没有参数,可以理解为定义在类中的普通函数
  • @staticmethod是装饰器
  • 静态方法只能操作类属性和其他类方法,不能操作实例属性和实例方法
  • 相比于自由方法,静态方法可以使用<类名>和<对象名>两种方式使用
  • 静态方法其实就是一种特殊的自由方法,只不过可以被实例对象调用

析构函数:

  • 双下划线的del(self)表示析构函数
  • 使用del删除对象且对象被真实删除时调用析构函数__del__()
  • 当实例对象被真实删除时,才调用该函数内语句
  • 真实删除:当前对象的引用数为0或当前程序退出(垃圾回收)
class DemoClass:
    count = 0 #公开类属性
    __count1 = 0 #私有类属性
    def __init__(self.name): #实例方法
    self.name = name
    DemoClass.count +=1
    
    @classmethod   #类方法
    def getchrCount(cls):
    s ='零一二三四五六七八九十'
    return s[DemoClass.count]

    @classmethod #用类方法访问私有属性
    def getCount1(cls):
    return DemoClass.__count
    
    def foo():   #自由方法
    DemoClass.count *=100
    return DemoClass.count
    
    @staticmethod  #静态方法
    def foo():
    DemoClass.count *=100
    return DemoClass.count

    def __len__(self): #保留方法
    return len(self.name)
    
    def __del__(self): #析构函数
        print"再见",self.name)

python面向对象复习笔记_第1张图片解释:在dc2=dc1这条语句中又创建了一个指向对象的指针,换句话说,当前对象的引用为2,del dc1只是删除了一个引用,并没有删除对象的全部引用,所以析构函数不会执行,只会在程序退出时默认要销毁dc2,所以才会执行。(在删除对象前,python解释器会检查引用次数)
sys.getrefcount(<对象名>)获得对象的引用次数

python类的封装

  • 属性的抽象:对类的属性(变量)进行定义,隔离以及保护
  • 方法的抽象:对类的方法(函数)进行定义,隔离以及保护
  • 目标是形成一个类对外可操作属性和方法的接口

封装Encapsulation:

  • 私有属性/方法:只能在类内部访问/使用
  • 公开属性/方法:可以通过类/对象名访问

用类方法访问私有属性可以对这个访问过程进行一定的控制,比如限制访问次数等。这就是用唯一接口去访问私有属性的好处。

多看一眼:私有属性

class DemoClass
    def __init__(self,name)
        self.__name=name

    def getName(self)
    return self.__name

dc1= DemoClass("老王")
dc2= DemoClass("老李")
print(dc1._DemoClass__name)

这段代码会正常输出:老王。原因是,实际上Python设计逻辑不明确支持私有,双下划线方法只是一种转换约定,转换后,类内原有名字发生了变化,这是一种形式上的私有

java or C++…是有专门的方式定义私有属性的,但是python没有。

类的保留属性/方法

仅用<类名>访问的保留属性

保留属性 描述
__name __ 类的名称
__qualname__ 以.分割从模块全局命名空间开始的类名称
__bases__ 类所继承的基类名称
<类>/<对象>.__dict__ 包含类成员/对象成员信息的字典,key是属性和方法名称,value是地址
__class__ 对象所对应的类信息
__doc__ 类描述,写在类定义的首行字符串,不能继承
__module__ 类所在模块的名称

python面向对象复习笔记_第2张图片
python面向对象复习笔记_第3张图片
python面向对象复习笔记_第4张图片

python类的继承

继承Inheritance:代码复用的高级抽象

  • 继承是面向对象程序设计的精髓之一
  • 实现了以类为单位的高抽象级别代码复用
  • 继承是新定义类能够几乎完全使用原有类属性与方法的过程
    python面向对象复习笔记_第5张图片
    python中类名后面的括号里的内容表示类继承的基类
  • 基类的属性基本等同于定义在派生类中
  • 派生类可以直接使用基类的类属性、实例属性
  • 派生类可以直接使用基类的各种方法
  • 使用基类的类方法和类属性时,要用基类的类名调用
  • 派生类只能继承基类的公开属性和方法
  • 派生类不能继承基类的私有属性和私有方法

object类是python所有类的基类(最基础的类是object,最基础的类型是type)

  • object是Python最基础类的名字
  • 所有类定义时默认继承object类
  • 保留属性和保留方法本质上是object类的属性和方法
函数/保留字 描述
id(x) 返回x的标识,Cpython用内存地址表示
x is y 判断x和y的标识是否相等,返回True/False,不判断值

类的属性重载

重载:派生类对基类属性或方法的再定义

  • 属性重载:派生类定义并使用了与基类相同名称的属性
  • 方法重载:派生类定义并使用了与基类相同名称的方法
    最近覆盖原则
  1. 优先使用派生类重定义的属性和方法
  2. 然后寻找基类的属性和方法
  3. 再寻找超类的属性和方法

增量重载:派生类扩展定义与基类相同名称的方法
super().<基类方法名>(<参数列表>)

类的多继承

class<类名>(<基类名1>,<基类名2>,…,<基类名N>):
从左到右的顺寻暗含了优先级大小,并且是深度优先规则,意思是从基类1一直找到object如果都没有要找的属性/方法,才会从基类2开始。

python类的运算

运算重载的限制:

  • 不能重载Python语言内置类型的运算符
  • 不能新建运算符,只能通过重载完成
  • is and not or 不能被重载
.__neg__(self) #取负
.__pos__(self) #取正
.__abs__(self) #取绝对值
.__invert__(self) #取反
.__add__(self,other) #加法
.__sub__(self,other) #减法
.__mul__(self,other) #乘法
.__truediv__(self,other) #除法
.__floordvi__(self,other) #整数除
.__mod(self,other)__ #模
.__divmod__(self.other) #对象除模
.__pow__(self,other) #幂的运算
.__lshift__(self,other) #左移
.__rshift__(self,other) #右移
.__lt/le/eq/ne/gt/ge__(self,other) #两个比较操作的运算重载< <= == != > >=

python类的多态

参数类型的多态:一个方法能够处理多个类型的能力
参数形式的多态:一个方法能够接受多个参数的能力
多态是OOP的一个传统概念,Python天然支持多态,不需要特殊语法
python面向对象复习笔记_第6张图片

引用Reference:对象的指针

  • 引用是内存中真实对象的指针,表示为变量名或内存地址
  • 每个对象存在至少1个引用,id()函数用于获得引用
  • 在传递参数和赋值时,Python传递对象的引用,而不是复制对象

python内部机制对引用的处理:

  • 不可变对象:immutable解释起为相同值维护尽量少的内存区域
  • 可变对象:mutable解释器为每个对象维护不同内存区域
    python面向对象复习笔记_第7张图片
    python面向对象复习笔记_第8张图片
    解释:很难在之间进行检验,所以只能动态输出结果,也就是给e新开辟一块内存空间。所以,逻辑是,对于不可变对象,python解释只能尽可能的做优化,但不一定只会生成一种拷贝。
    python面向对象复习笔记_第9张图片

导致引用+1的情况

  • 对象被创建:d=DemoClass()
  • 对象被引用:a=d
  • 对象被作为一个容器中的元素:ls=[d]

导致引用-1的情况

  • 对象被删除 del d
  • 对象的名字被赋予新的对象:d=123
  • 对象离开作用域:foo()函数的局部变量count
  • 对象所在容器被删除:del Is

引用Reference:对象的指针

  • 引用是内存中真实对象的指针,表示为变量名或内存地址
  • 在传递参数和赋值时,Python传递对象的引用,而不是复制对象
  • 不可变对象与可变对象的内存管理略有不同。

浅拷贝和深拷贝

  • 拷贝:复制一个对象为新的对象,内存空间有“变化”
  • 浅拷贝:仅仅复制最顶层对象的拷贝方式,默认拷贝方式
  • 深拷贝:迭代复制所有对象的拷贝方式

浅拷贝详解:
python面向对象复习笔记_第10张图片
深拷贝:
完全拷贝对象内容

  • 采用copy库的deepcopy()方法
  • 迭代拷贝对象内各层次对象,完全新开辟内存建立对象
  • 深拷贝仅仅针对可变类型,不可变类型无需创建新对象

类的方法引用

  • 实例方法名也是一种引用,即对方法本身的引用
  • 当方法被引用时,方法(即函数)将产生一个对象:方法对象
    python面向对象复习笔记_第11张图片

python类的高级话题

命名空间的理解

命名空间Namespace:从名字到对象的一种映射

  • 作用域:全局变量名在模块命名空间,局部变量名在函数命名空间
  • 属性和方法在类命名空间,名字全称:<命名空间>.<变量/函数名>
  • 命名空间低层由一个dict实现,变量名是键,变量的引用的对象是值
    python面向对象复习笔记_第12张图片
    python面向对象复习笔记_第13张图片

类的特性装饰器

比如一个人的年龄不可能是负数,但你给这个属性赋值为负时,并不能检测异常值,这就是个问题,如何为属性增加异常检测。

@property:类的特性装饰器

  • 使用@property把类中的方法变成对外可见的"属性"
  • 类内部:表现为方法
  • 类外部:表现为属性

@<方法名>.setter
用于设定属性的赋值操作
python面向对象复习笔记_第14张图片

自定义的异常类型

异常Exception也是一种python类

  • try-except捕捉自定义的异常
  • 继承Exception类,可以给出自定义的异常类
  • 自定义异常类是类继承的正常应用过程

python面向对象复习笔记_第15张图片

类的名称修饰

名称修饰Name Mangling:类中名称的变换约定

  • python通过名称修饰完成一些重要功能
  • 采用下划线(_)进行名称修饰,分为5种情况
  • _x,x_,__x,__x__,_

_x:单下划线开头的名称修饰

  • 单下划线开头属性或方法为类内部使用 PEP8
  • 只是约定,仍然可以通过<对象名>.<属性名>方式访问
  • 功能:from XX import *时不会导入单下划线开头的属性或方法
  • 就是告诉程序员这个一般在内部使用,单你要强制,也可以。

x_:单下划线结尾的名称修饰

  • 单下划线结尾或方法为避免与保留字或已命名冲突
  • 只是约定,无任何功能性对应

__x:双下划线开头的名称修饰

  • 双下划线开头属性或方法将被解释器修改名称,避免命名冲突
  • 不是约定,而是功能性,实现私有属性,私有方法
  • __x会被修改为:_<类名>.__x

python面向对象复习笔记_第16张图片

__x__:双下划线开头和结尾的名称修饰

  • 双下划线开头个结尾的属性或方法无任务特殊功能,名字不能被修改
  • 部分名称时保留方法或保留属性

_:单下划线

  • 单下划线是一个无关紧要的名字,无特殊功能

python面向对象复习笔记_第17张图片

python的最小空类

python最小空类的作用:

  • 类是一个命名空间,最小空类可以当作命名空间使用
  • 最小空类可以辅助数据存储和使用
  • 动态增加属性是python类的一个特点
    python面向对象复习笔记_第18张图片

你可能感兴趣的:(python)