目录
- 面向对象
- 为什么要面向对象?
- 要素
- 定义
- 一些特性
- 内存空间
- 实例的创建与初始化
- 创建实例方法__new__
- 初始化实例方法__init__
- 类的继承
- 多态
- 新式类与经典类
面向对象
为什么要面向对象?
方便版本更新迭代,程序结构清晰明了
要素
类:用来描述具有相同属性和方法的对象的集合
属性:对象的特征
方法:对象的行为
对象实例:类的一个具体的例子
class Student:
# 属性
school = "清华大学"
# 方法
def eat(self):
print("eat meal")
# 实例化
zhangsan = Student()
print(zhangsan.school)
zhangsan.eat()
定义
类在python2中分为新式类和经典类,在python3中只有新式类
新式类:继承了Object类的类
类的命名遵循大驼峰规则
# 类定义的三种方式
class A():
pass
class A:
pass
class A(object): # 指定父类
pass
一些特性
class ATM():
bank="建设银行"
money = 50000
def draw_money(self,getmoney): # 函数的命名遵循小写加下划线的原则
if getmoney <= self.money: # 与java不同,在类的方法中,不能直接使用类的属性,无法识别,需要通过self来进行调用
self.money -= getmoney # self并非一定要取self,其它名字也可以,调用函数时无需我们传入,py解释器会自动传入当前实例
print(f"取了{getmoney}元,还剩余{self.money}元")
else:
print("atm余额不足")
def test(): # 当函数没有传入self,则不视为方法,而只是类中的一个函数,无法被实例调用,但可以通过类来调用
pass
nongda_atm = ATM()
print(nongda_atm.money)
nongda_atm.draw_money(3000)
nongda_atm.draw_money(3000)
ATM.test()
ATM.provide = "湖南" # 可以直接添加一个新的类的属性并且修改
print(nongda_atm.provide)
内存空间
分为类空间和实例空间
当实例用到某个属性或者方法时会先在自身实例空间找,找不到就去类空间找,并复制一份到自身实例空间,还找不到就去父类空间找,都找不到报 AttributeError属性异常
创建实例的时候,会有一个类对象指针,通过这个指针,实例就能访问类的属性方法了
实例的创建与初始化
创建实例方法__new__
当自定义类没有重写__new__方法时,会调用父类object的new方法(因此新式类才有–new–)
new方法一般也不会进行重写
class ATM():
# 类属性
money = 50000
def __new__(cls,*args,**kwargs): # arg用于接收参数传给init
# cls代表当前类
print("this is new")
print("cls is",cls)
print("args is",args)
return object.__new__(cls) # 最后还是调用object的new方法,会返回一个实例对象self
初始化实例方法__init__
在new方法执行完后对创建好的实例进行初始化(如果new方法返回的不是当前类的实例,init不执行)
class ATM():
# 类属性
money = 50000
def __init__(self, school="qinghua"):
# 实例属性
self.school = school
print("i am init")
# 测试
q_atm = ATM()
print(q_atm.school)
n_atm = ATM("nongda")
print(n_atm.school)
# print(ATM.school) # 类空间中并没有school属性
类的继承
好处:增加代码复用率
python中可以继承多个父类,用逗号分隔
class Animal():
def __init__(self):
pass
class Person(Animal): # 继承Animal类
def __init__(self): # 重写父类方法
super().__init__() # super()调用父类的方法,不是必要的,一般都放在第一行,避免自定义属性被覆盖
self.name = "Person"
p = Preson()
print(isinstance(p, Person),isinstance(p, Animal)) # True,True --判断实例是否是某个类的实例
print(type(p)) # <class '__main__.Person'> --查看类型
print(Person.__base__) # <class '__main__.Animal'> --查看继承关系
多态
接口的多种不同实现方式即为多态。
python的多态不同于其它语言的多态,其它语言多是通过继承父类和重写父类方法来实现多态。而python本身就是一种动态的语言,处处都是多态。
class ZhiFuBao(){
def pay(self){
print("zhifubao")
}
}
class WeiXin(){
def pay(self){
print("weixin")
}
}
# 接口
def pay(obj){ # 特别之处就在于此,python不关心对象的类型,只关心行为
obj.pay() # 并且不管什么类型,只要有pay()方法就行
}
z = ZhiFuBao()
w = WeiXin()
# 通过相同的接口,不同的实例有不同的实现
pay(z)
pay(w)
新式类与经典类
区别一:继承了内置类的类都是新式类,python3后都是新式类
区别二:(重点)对于父类的解析顺序有所不同,经典类为深度优先;新式类则是C3算法————深度优先+子类优先,同级别父类从左到右考虑
C3算法:
首先将自身加入到本序列,然后对继承序列的元素依次判断
若某元素不在其他序列或者他是所有继承序列的第一个,那么就把这个元素提取到本序列
C3算法将每个分支节点的父类按照上面的原则得到的顺序合并为一个列表,并将当前节点加入到列表的最前面。
C3算法的实现由
super()
函数来完成,它会自动按照MRO(方法解析顺序)调用父类的方法。
对于上面的继承关系(A为所有类的父类),
在经典类中,F的方法解析顺序为:F——》D——》B——》A——》E——》C
在新式类中,解析顺序则为:F——》D——》B——》E——》C——》A
本来按照深度优先来说A应该在E前面,但根据子类优先来说,E、C为A的子类,所以先解析