自己以前整理的笔记,不太完整,后续会不断更新。。。。
- [ ] __new__方法扩展
- [ ] 魔法方法
- [ ] 什么情况下使用self,什么情况不用?
面向过程和面向对象是两种不同的编程方式
一、面向过程
过程:是早期的一种编程概念,类似于函数,但只有执行,没有返回值
面向过程:把能实现某些独立功能的代码封装成一个个函数,然后顺序调用不同的函数
特点
- 过程与步骤---怎么做?
- 若需求复杂,代码也会变复杂
二、面向对象(Object Oriented Programming)
具体的事物抽象化,抽象的事物具象化---前者针对实际存在的事物,后者针对抽象的事物
特点
- 相比于函数,面向对象是更大的封装,一个对象可根据职责封装多个方法
- 注重对象和职责---谁来做?
- 适合复杂项目开发,提供固定的套路
对象的三大特性
- 封装:封装属性和方法
- 继承:实现代码的重用
- 多态:对封装和继承的功能扩展:对象调用的方法,在子类和父类中都可以有,父类中有,子类可重写,由此不同的子类可实现不同的功能
三、类
- 抽象
- 属性
- 方法
类用来创建对象
对象是类的具象、实例化
类只有一个,对应的对象有多个:不同对象之间的属性可能不同
三要素
- 类名:大驼峰命名
- 属性:特征
- 方法:行为
01.对象
- 每一次实例化的对象所保存的地址都不同
- 临时为对象添加属性的方法:直接给对象赋值 -----不推荐,未修改类
dir()内建函数
使用dir()可传入任意对象,查看对象的所有属性及方法
显示出的__方法名__格式的方法是python提供的内置属性/方法
对象初始化-引用
- 创建对象后,变量保存的是对象在内存中的地址
- 变量引用(指向)了新建的对象
- print打印变量,显示该变量引用的对象是由哪一个类创建的对象,及内存中的地址
对象创建过程-原理
- 当使用 类名( ) 创建对象时,会自动执行以下操作:
- 为对象在内存中 分配空间 —— 创建对象
- 为对象的属性 设置初始值 —— 初始化方法 _init_()
- 这个 初始化方法 就是
__init__
方法,__init__
是对象的内置方法,创建对象时自动调用该方法
__init__
方法是 专门 用来定义一个类 具有哪些属性的方法!
self
- 由 哪一个对象 调用的方法,方法内的self就是 哪一个对象的引用
- 对象调用方法时,不需要传递self参数
- 类中定义方法时可通过self.的方式访问属性及调用其他方法
02.内置方法
_init_()
当使用 类名( ) 创建对象时,分配完内存空间后,自动调用该方法
- 创建对象时类名( )中的实参传入到__init__( )中除self之外的其他形参中
- 若属性有初始值则不适合作为形参在对象初始化的时候传入,可在该方法内直接赋值
- 若某属性的初始值不确定,可在该方法内赋值为None
_del_()与del
当一个对象被从内存销毁前,会自动调用该方法
class Cat:
def __init__(self, new_name):
self.name = new_name
print('初始化调用')
def __del__(self):
print('销毁前调用')
# 整个程序结束后对象才会被销毁
tom = Cat('Tom')
print(tom.name)
# del tom # 使用del,提前调用__del()销毁对象
print('_'*50)
_str_()
定制化print出的内容
- 必须要return 一个字符串
- 用于print()对象时的显示结果
- 如果不定义该方法,打印对象时返回结果为十六进制内存地址
__class__属性
每一个实例对象中都存在一个__class__属性,指向创建该对象的类对象
03.私有属性和私有方法
定义
私有属性/私有方法:在属性名称/方法名称前加__(两个下划线)
特点
私有属性和方法无法在类外访问,只能在类中访问
伪私有:python中不存在真正意义上的私有,私有属性和方法可以通过以下格式访问
对象._类名.__属性/方法
实际上是python对私有属性和方法改名了
class Women:
def __init__(self, name):
self.name = name
# 不要问女生的年龄
self.__age = 18
def __secret(self):
print("我的年龄是 %d" % self.__age)
xiaofang = Women('小芳')
print(xiaofang._Women.__age)
xiaofang._Women.__secret()
注意:
# *-* coding:utf-8 *-*
# 私有属性只能在类中定义
class Test(object):
def __init__(self, name):
self.__name = name
a = Test('老王')
print(a._Test__name) # 可访问
print(a.__dict__) # 查看a对象的所有属性
# 实际上是__name被解释器名字重整为_Test__name
a.__name='老李' # 添加一个属性,属性名为__name
print(a.__name)
print(a.__dict__)
# 结果
老王
{'_Test__name': '老王'}
老李
{'_Test__name': '老王', '__name': '老李'}
四、继承
概念:子类拥有父类的所有属性和方法
子类在自己方法内不能直接访问父类的私有属性和私有方法,但可以通过父类的公有方法间接访问私有属性和方法
属性
- 继承的传递性:‘孙类’不仅继承父类的属性和方法,还会继承‘爷类’的属性和方法
01. 多重继承
子类方法的重写:
父类的方法实现无法满足子类的需求
- 覆盖父类方法
- 对父类方法进行扩展:
- super( ).方法( ): super( )的主要作用是扩展父类中已有方法的功能
若父类中都存在某方法,则一直向上找,直到找到为止
也就是说向上只能找到第一个某方法
- super(class1,self).方法1():继承class1父类的方法1
- 父类名.方法(self): 调用父类中的方法,若父类中没有该方法则去父类的父类中查找
若所有父类中都存在同一名称的方法,那么可以用这种方法调用特定类中的方法
super().__init__相对于 类名.__init__,在单继承上用法基本无差,但在多继承上有区别,super方法能保证每个父类的方法只会执行一次,而使用类名的方法会导致方法被执行多次,如下的案例:
http://www.cnblogs.com/dkblog/archive/2011/02/24/1980654.html
02. 多继承
class B:
pass
class C:
pass
class A(B, C):
pass
开发时若多个父类中的属性或方法名称相同时,应尽量避免使用多继承,否则容易混淆
python中针对类提供了内置属性__mro__可以查看子类对多继承的父类的搜索顺序
print(A.__mro__)
# 结果
# (, , , )
- 在搜索方法时,是按照
__mro__
的输出结果 从左至右 的顺序查找的 - 如果在当前类中 找到方法,就直接执行,不再搜索
- 如果 没有找到,就查找下一个类 中是否有对应的方法,如果找到,就直接执行,不再搜索
- 如果找到最后一个类,还没有找到方法,程序报错
object类是python中所有类的基类,提供一些内置的属性和方法
新式类与旧式(经典)类
- 新式类:
默认以object类为基类
多继承时,搜索父类时采用广度优先原则
python3.x中都是新式类
- 经典类:
不默认以object类为基类
多继承时,搜索父类时采用深度优先原则
python2.x中,若不指定父类,不会以object类作为基类
建议:为保证代码在2.x和3.x中均能运行,只要没有父类,都写上object作为父类
当所有父类都有共同的父类时,在到达共有父类之前先按深度优先原则,再按广度优先原则进行继承
几种多继承方式:
一个坑
# 新式类继承顺序,广度优先原则
class A:
pass
class B(A):
pass
# 报错:继承顺序回头,C--->A--->B--->A
class C(A,B):
pass
print(C.__mro__)
# 正常:继承顺序,C--->B--->A--->?
class C(B,A):
pass
print(C.__mro__)
五、多态
不同的子类对象调用相同的父类方法,产生不同的执行结果
特点:
- 多态可增加代码的灵活度
- 以继承和重写父类方法为前提
- 不影响父类的内部
多态的好处就是,当我们需要传入Dog、Cat、Tortoise……时,我们只需要接收Animal类型就可以了,因为Dog、Cat、Tortoise……都是Animal类型,然后,按照Animal类型进行操作即可。由于Animal类型有run()
方法,因此,传入的任意类型,只要是Animal类或者子类,就会自动调用实际类型的run()
方法,这就是多态的意思:
对于一个变量,我们只需要知道它是Animal类型,无需确切地知道它的子类型,就可以放心地调用run()
方法 ,而具体调用的run()
方法是作用在Animal、Dog、Cat还是Tortoise对象上,由运行时该对象的确切类型决定,这就是多态真正的威力:调用方只管调用,不管细节,而当我们新增一种Animal的子类时,只要确保run()
方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:
**对扩展开放:允许子类重写父类方法函数 **
**对修改封闭:不重写,直接继承父类方法函数 **
六、类属性和类方法
从类的实例化讲起:
每次通过 类名( ) 创建对象的动作有两步:
在内存中为对象 分配空间
调用初始化方法
__init__
为 对象初始化
对象创建后,内存 中就有了一个对象的 实实在在 的存在 —— 实例
对象的属性叫实例属性,对象调用的方法叫实例方法
01 内存空间分配
如图,一个类可实例化为多个对象,但
- 每一个对象 都有自己 独立的内存空间,保存各自不同的属性
- 多个对象的方法,在内存中只有一份,保存在类所在的内存空间,在调用方法时,需要把对象的引用 传递到方法内部即可
- 在程序运行时,类会被加载到内存
- 每个实例对象内都存在一个内置属性__class__,保存着该属性是由哪个类创建的
02 类是一个特殊的对象
class A: 定义的类属于类对象
obj=A( ): 定义的对象属于实例对象
- 类对象在内存中只有一份
- 类对象也有自己的属性和方法,分别叫类属性 和类方法
- 通过 类名. 的方式可访问类属性和类方法
类对象与实例对象互相访问权限
- 类对象-->实例对象
类对象不可以访问实例对象的属性和方法
- 实例对象-->类对象
实例对象可以访问类属性(前提:实例对象中无同名属性)和类方法(且实例方法不能与类方法重名,否则无效,只调用类方法) ,
实例对象也可访问静态方法
03 类属性
在类对象中定义的属性,通常会用来记录与该类有关的特征,而不会用于记录具体对象的特征
应用场景:通过类创建实例对象时,如果每个对象需要具有相同名字的属性,那么就使用类属性,用一份既可
python中实例对象属性的获取存在一个向上查找的机制:
- 通过
对象名.属性名
方式调用属性时,首先在实例对象内部查找 - 没找到就会向上寻找同名的类属性
所以,实例对象也可以通过对象名.类属性
的方式访问类属性(但不推荐)
注意:
如果给对象以对象名.类属性=值
的方式添加属性,不会影响类属性的值,只会给对象新建一个与类属性同名的实例属性
实例对象也可以通过self.__class__.类属性
给类属性重新赋值
04 类方法
类方法 就是针对 类对象 定义的方法
在类方法内部可以直接访问类属性或者调用其他的类方法
# 定义类方法
@classmethod
def 类方法名(cls, var1, var2):
pass
类方法形参第一个参数应该是cls
,其效果类似于实例方法的第一个形参self
,哪一个类调用该方法,方法的cls
就是那个类的引用
05 静态方法
在类中既不需要访问实例属性和方法,也不需要访问类属性和方法的方法叫静态方法
# 定义静态方法
@staticmethod
def 静态方法名():
pass
通过类名.
调用静态方法,实例对象也可以调用静态方法
七、单例
单例设计模式
设计模式
- 是 前人工作的总结和提炼,通常,被人们广泛流传的设计模式都是针对 某一特定问题 的成熟的解决方案
- 使用 设计模式 是为了可重用代码、让代码更容易被他人理解、保证代码可靠性
单例设计模式
让类创建的对象,在系统内存中只有唯一的一个实例
即:每次执行新建对象操作所返回对象的内存地址是相同的
应用:音乐播放器、打印机.....
01.__new__(cls)
使用类创建对象时,python解释器先调用__new__( )给对象分配内存空间,然后再调用__init__( )初始化
__new__( )是object基类提供的内置静态方法,其功能:
- 在内存中为对象分配空间
- 返回对象的引用
python解释器获得其返回的对象引用后,将其作为参数传递给__init__( )方法的第一个参数self
由以上可见:
- 要实现单例设计模式,可通过重写__new__( )方法来控制创建对象时内存空间的分配
- 基类object
super().__new__(cls)
可以帮助实现对象内存的分配,并返回对象的引用,也就是说,此时super().__new__(cls)
是一个对象的引用 - 重写
__new__
方法 一定要return super().__new__(cls)
,因为它可以帮助实现内存空间分配的功能 - 调用时需要主动传递cls参数,传递的是类对象
- 由于要识别对象是否是初次调用,所以需要有一个类属性来记录实例对象的实例化次数
class MusicPlayer:
count = None
def __init__(self):
print('初始化播放器对象')
def __new__(cls):
if MusicPlayer.count is None:
MusicPlayer.count = super().__new__(cls) # 基类object中的__new__方法完成了内存的分配并返回了对象的引用
print(MusicPlayer.count)
return MusicPlayer.count # 要返回对象的引用
player1 = MusicPlayer()
player2 = MusicPlayer()
print(player1)
print(player2)
02.__new__方法扩展
两个用法:用于重写一些不可变类型数据的类如:str,int,tuple;元类?????