目录
一、变量进阶
变量的引用
- 变量 和 数据 都是保存在 内存 中的
- 在 python 中 函数 的参数传递 以及 返回值 都是靠 引用 传递的
引用的概念
- 变量 和 数据是分开存储的
- 数据保存在内存中的一个位置
- 变量中保存着数据在内存的地址
- 变量中记录数据的地址,就叫引用
- 使用 id() 函数可以查看变量保存数据所在的内存地址
- 注意:如果变量已经被定义,当给一个变量赋值的时候,实质是修改了数据的引用
- 变量不再对之前的数据引用
- 变量改为对新赋值的数据引用
函数的参数和返回值的传递
- 函数的实参/返回值 都是靠引用来传递的
- 传递的都是数据在内存中的地址(即引用),而不是数据本身
可变和不可变类型
- 不可变类型,内存中的数据不允许被修改:
- 数字类型 int,float,bool,complex,long(z,x)
- 字符串 str
- 元组 tuple
- 可变类型,内存中的数据可以被修改
- 列表 list
- 字典 dict
- 注意:
- 字典的key只能使用不可变类型的数据
- 可变类型的数据变化,是通过方法来实现的
哈希
- 接收一个不可变类型的数据作为参数
- 返回结果是一个整数
- 哈希是一种算法,作用是提取数据的特征码
- 相同的内容得到相同的结果
- 不相同的内容得到不相同的结果
- 在python中,设置字典的键值对是,会首先对key进行hash已决定如何在内存中保存字典的数据,方便后续对字典的操作:增、删、改、查
- 键值对的key必须是不可变类型数据
- 键值对的value可以是任意类型的数据
缺省参数
- 定义函数时,可以给某个参数指定一个默认值,具有默认值的参数就叫做缺省参数
- 调用函数时,如果没有传入缺省参数的值,则在函数内部使用定义函数时指定的参数默认值
- 使用最常见的值作为默认值
- 如果一个参数的值不能确定,则不应该设置默认值,具体的数值在调用函数时,由外界传递
- 注意:
- 缺省参数的定义位置必须保证在参数列表末尾
- 调用带有多个缺省参数的函数,需要指定参数名
多值参数
- 有时候可能需要一个函数能够处理的参数个数是不确定的,这个时候就可以使用多值参数
- 参数名前增加一个*可以接收元组,一般使用*args——存放元组参数
- 参数名前增加两个*可以接收字典,一般使用**kwargs——存放字典参数
元组和字典的拆包
- 在调用带有多值参数的函数时,希望将一个元组变量,直接传递
函数的递归
- 在函数内部自己调用自己,函数内部的代码是相同的,只是针对参数不同,处理的结果不同
- 必须要有递归的出口,当参数满足某个条件时,不再执行函数,如果没有出口会出现死循环
二、面向对象(OOP)
- 相比较函数,面向对象是更大的封装,根据职责在一个对象中封装多个方法
- 使用与复杂项目开发,提供固定套路
(一)概念
类的概念——相当于一张图纸
- 类是对一群具有相同特征或者行为的事物的一个统称,是抽象不能直接使用的
- 特征被称为属性,行为被称为方法
- 设计类:
- 类名:满足大驼峰命名法
- 属性:这类事物具有什么样的特征
- 方法:这类事物具有什么样的行为
对象的概念——用图纸制造出来的物体
- 对象是由类创建出来的一个具体存在,可以直接使用
- 有哪个类创建的对象,就拥有哪个类中定义的属性和方法
(二)面向对象基础语法
dir内置函数
- 可查看对象内的所有属性及方法
- __方法名__格式的方法是python提供的内置方法/属性
定义类
class 类名:
def 方法1(self,参数列表):
pass
def 方法2(self,参数列表):
pass
- 注意:第一个参数必须是self,哪一个对象调用的方法,self就是哪一个对象的引用(对象的内存地址)
- 在方法内部,可以通过self访问对象的属性,和调用其他的对象方法
创建对象
对象变量 = 类名()
一个类可以创建多个对象
注意:在日常开发中,不推荐在类的外部给对象增加属性,如果在运行时没有找到属性,程序会报错,对象应该包含有哪些属性,应该封装在类的内部
(三)封装
- 是面向对象编程的一大特点
- 将属性和方法封装到一个抽象的类中
- 外界使用类创建对象,然后让对象调用方法
- 对象方法的细节都被封装在类的内部
- 在对象的方法中,是可以直接访问类的属性
- 同一个类创建的多个对象之间,属性互不干扰
- 一个对象的属性可以是另外一个类创建的对象
身份运算符
身份运算符用于比较两个对象的内存地址是否一致——是否是对同一个对象的引用
针对None比较时,建议使用is判断
运算符 | 描述 | 实例 |
---|---|---|
is | is是判断两个标识符是不是引用同一个对象 | x is y 类似 id(x) == id(y) |
is not | is not 是判断两个标识符是不是引用不同对象 | x is not y, 类似id(a) != id(b) |
is 与 == 区别
is用于判断两个变量引用对象是否同一个
== 用于判断引用变量的值是否相等
私有属性和私有方法
- 对象的某些属性或方法可能只希望在对象的内部被使用,而不希望在外部被访问到
- 私有属性就是对象不希望公开的属性
- 私有方法就是对象不希望公开的方法
定义方式
- 在定义属性或方法时,在属性名或者方法名前增加两个下划线,定义的就是私有属性或方法
(四)继承
- 继承实现代码的重用,相同的代码不需要重复的编写
- 子类拥有父类的所有方法和属性
实例语法
class 类名(父类名):
pass
- 子类继承父类,可以直接使用父类中已经封装好的方法,不需要再次开发
- 子类中应该根据职责,封装子类特有的属性和方法
- 专业术语
- 子类 == 派生类 父类 == 派生类 继承 == 派生
- 继承具有传递性,子类可以继承爷爷类
方法的重写
两种情况:
1)覆盖父类的方法
父类的方法实现和子类的方法实现完全不同,就可以使用覆盖的方式,在子类中重新填写父类的方法实现,重写后,只会调用子类中重写的方法,而不会调用父类封装的方法
2)对父类方法进行扩展
子类的方法实现中包含父类的方法实现,父类原本封装的方法实现是子类方法的一部分(即子类中还需要用到父类的方法,而又想再定义更多的方法)
使用方法是 super().父类方法 来调用父类方法的执行
- 当父类的方法实现不能满足子类需求是,可以对方法进行重写
父类的私有属性和私有方法
- 子类对象不能再自己的方法内部,直接访问父类的私有属性或私有方法
- 子类对象可以通过父类的公有方法间接访问到私有属性或私有方法
(五)多继承
- 子类可以具有多个父类,并且拥有所有父类的属性和方法
- 语法实例:
class 子类名(父类1, 父类2, ...)
注意:如果不同的父类中存在同名的方法,子类对象调用方法时,会调用哪一个父类的方法呢?
开发时,应该尽量避免这种容易产生混淆的情况,如果父类之间存在同名的属性或者方法,应该尽量避免使用多继承
python中的MRO——方法搜索顺序
- 提供一个内置属性__mro__可以看到方法搜索顺序
- MRO主要用于多继承时判断方法、属性的调用路径
新式类和旧式类
- object是python为所有对象提供的基类,提供有一些内置的属性和方法,可以使用dir函数查看
- 新式类:以object为基类的类,推荐使用
- 旧式类:不以object为基类的类,不推荐使用
- 在python 3.x中定义类时,如果不指定父类,就会默认使用object 为基类,所以都是新式类
- 在python 2.x中定义类时,如果不指定父类,就不会以object为基类,所以是旧式类
- 为保证同时在python 2.x 和 3.x中运行,定义类时,统一继承自object
(六)多态
不同的子类调用相同的父类时,产生不同的执行结果
- 多态可以增加代码的灵活性
- 以继承和重写父类方法为前提
- 是调用方法的技巧,不会影响类的内部设计
术语
- 创建出来的对象叫做类的实例
- 创建对象的动作叫做实例化
- 对象的属性叫做实例属性
- 对象调用的方法叫做实例方法
类是一个特殊的对象
- 在程序运行时,类同样会被加载到内存中
- 类是一个特殊的对象——类对象
- 程序运行时,类对象在内存中只有一份,使用一个类可以创建很多个对象实例
- 除了封装实例的属性和方法,类对象还可以拥有自己的属性和方法——类属性、类方法
- 类属性:
- 就是给类对象中定义的属性
- 通常用来记录与这个类相关的特征(模板)
- 类属性不会用于记录具体对象的特征(实例)
- 属性的获取机制——向上查找机制
- 1.类名.类属性
- 2.对象.类属性(不推荐)
- 注意:使用对象名.类属性 = 值 赋值语句,只会给对象添加一个属性,而不会影响到类属性的值
- 类方法:
- 定义类方法:需要用修饰器@classmethod来标识,告诉解释器这是一个类方法,第一个参数应该是cls,与self类似
@classmethod
def 类方法名(cls):
pass
静态方法
如果需要封装一个方法,即不需要访问实例属性或者调用实例方法,也不需要访问类属性或者调用类方法,就可以封装成静态方法
@staticmethod
def 静态方法名():
pass
通过 类名. 的方法直接调用静态方法,不需要创建对象
类属性和实例属性的区别:类属性就相当于全局属性,比如记录该类创建了多少个对象;实例属性就相当于局部属性,每个对象的属性都不一样,互不影响(实例属性下初始化方法中定义)
五大概念:类属性、类方法、实例属性、实例方法、静态方法
(七)单例
单例设计模式
- 让类创建的对象,在系统中只有唯一一个实例
- 每一次执行 类名()返回的对象,内存地址是相同的
__new__方法
- 功能
- 在内存中为对象分配空间
- 返回对象的引用
- 重写__new__方法一定要return super().__new__(cls)
- 否则解析器得不到分配了空间的对象引用,就不会调用对象的初始化方法
- __new__是一个静态方法,在调用时需要主动村传递cls参数
创建单例
- 定义一个类属性,初始值是None,用于记录单例对象的引用
- 重写__new__方法
- 如果类属性is None ,调用父类方法分配空间,并在类属性中记录结果
- 返回类属性中记录的对象引用
初始化工作只执行一次
- 定义一个类属性 init_flag 标记是否执行过初始化动作,初始值为False
- 在__init__方法中,判断init_flag, 如果为false就执行初始化动作
- 然后将init_flag设置为True
- 这样,再次自动调用 __init__ 方法时,初始化动作就不会被再次执行了