面向对象
类:把一类事物的相同的特征和动作整合到一起就是类,类是一个抽象的概念
对象:就是基于类而创建的具体的事物(真实存在的),也是特征和动作整合到一起
注意:python是一个面向对象的语言,但是不会强制你去按照面向对象语言的思路去设计写程序
面向对象的设计 -------》 面向对象 就是一个面向对象实现和升级方便的改变
def dog(name,age,type_dog):
#定义了一个函数,该函数产生狗这个类
def jiao(x):
print('狗%s在汪汪汪'%x)
def chi(x):
print('狗%s在吃狗粮'%x)
def gou(name,age,type_dog):
gou_t ={'name':name,
'age':age,
'type_dong':type_dog,
'chi':chi,
'jiao':jiao
}
return gou_t
return gou(name,age,type_dog)
gou1 = dog('dahuang',14,'jinmao')
print(gou1)
gou1['chi'](gou1['name'])
#面向对象设计 在把狗这个对象(包含属性,和调用方法)和其动作(调用方法)放在同一
#个二级作用域下面,只有自己能调用该方法,实现捆绑了。在字典表示实例(对象)特征时候,把函数
#放进去(动作),就把动作也整合到一起了。
class dog: #类不需要写参数
def __init__(self,name,age,typ_dog): #self是实例本身,
self.mingzi=name
self.nianling=age
self.zhonglei=typ_dog #实例化的函数,当类要实例化时候,自动转到__init__函数运行,自动return self
def jiao(self):
print('狗%s在汪汪汪'%self.mingzi) #注意这里可以不写self,但是当用对象去调用的时候,类(class)会自动把类当做参数
def chi(self): #传到函数方法中,就会出现报错(定义没有参数,你传了参数)
print('狗%s在吃狗粮'%self.mingzi)
dog1 = dog('wangcai','2','labuladuo') #self参数就是dog1
dog1.jiao() #dog1先去自己的作用域找(函数__init__)中找,找不到再去类(相当于函数)中找。
#注意,根据上面可知,其实对象只有数据属性,没有函数属性。函数属性都是在类中。对象去调用属性,是在本身的外边去调用,非自己本身字典()
#作用域中有的
#查
print(dog1.mingzi)
#增
dog1.haha='test' #增加数据属性
def test(self,x): #定义函数
print('增加函数%s'%self.mingzi)
dog.test =test #增加类函数属性
dog1.test('x')
#改
def test(self,x): #定义函数
print('增加函数%s'%self.mingzi)
dog.chi =test #把chi函数改变成test函数
#删
del dog.chi #删除函数属性
#对于实例的增删改查 我们不要把实例里面加入函数属性 实例里面只有数据属性 加了函数属性以后实例化的过程中,都要重复去执行函数属性
一般没人去这么干
#对于实例的改 我们不要用字典 dog.__dict__[]='zhi' 去增加,改变底层的实例属性
#注意我们再类里面定义的都是属性,那就是类特有的,如果外面在定义一个相同的变量 这个时候我们要考虑
1.对象想用这个变量的时候,直接使用变量是跟这个类毫无关系的,它会去最外层找 如果是用类方法 即 实例.变量 则会到类里面找这个变量
找不到就停止报错
2.用.去调用就是跟实例类有关,其他方式调用跟他没关系
类
class chinese:
dang = 'gcd' #数据属性
def chi(self): #函数属性
print('shuidao')
pass
print(chinese)
print(chinese.dang) #类中用.来调用类
chinese.chi('dasdad')
print(chinese.__dict__) #类的属性字典,其实上面的调用的过程就是直接取属性字典
class room:
def __init__(self,chang,kan,aaa):
self.Length=chang
self.Width=kan
self.gaoo=aaa
#静态属性
@property #把下面类函数属性封装了一下,变成数据属性
def cal_mianji(self): #我们有计算面积的需求,就弄了一个函数属性
return self.Length*self.Width #用property把调用方法改了,用户调用的时候
#感觉跟数据属性一样,但其实是函数属性
#类属性
@classmethod
def lei(cls,x): #自动填入类自身 绑定类
print('%s方法%s'%(cls,x)) #类方法的目的就是想不创建实例,直接使用方法,跟实例没半毛钱关系。
#静态方法
@staticmethod
def test(x,y): #静态方法其实就是一个工具包,跟类和实例其实没多大关系,无法去调取他们的属性(默认不
print(x,y) #不传类和实例参数,因此无法访问他们属性)
def test2(x,y): #test静态方法和test2定义的函数是有区别的,当类调用的时候,他们是一样的,但是实例调用
print(x,y) #的时候,默认会把实例传入test2参数中,test2还是捆绑了实例 而test什么都不捆绑
r1 = room(10,10,5)
print(r1.cal_mianji)
print(r1.Length) #两个调用完全一样
room.lei('x') #类方法的调用
r1.lei(r1) #实例也可以调用类方法,但是没意义,其本身目的就是用类去掉用,不碰实例
room.test(1,2)
r1.test(1,2) #静态方法调用,静态方法本身跟类和属性没关系
#组合
class school:
#学校类
def __init__(self,name,addr,studens):
self.name=name
self.addr=addr
self.studens=studens
def add_studens(self):
print('%s开始招生'%self.name)
class course:
#课程类
def __init__(self,name,price,time,school,teacher):
self.name=name
self.price=price
self.time=time
self.school=school #这里其实就是把值等于实例(对象),就相当于
self.teacher=teacher #函数高阶函数,在函数里嵌套一个函数。这样的
#作用会使两个独立的对象关联起来
class teacher:
#老师类
def __init__(self,name,age,school):
self.name=name
self.age=age
self.school=school
sh1 = school('老男孩','北京',200)
tc1 = teacher('alex',35,sh1)
cs1 = course('python',20000,'100days',sh1,tc1) #传了两个对象在另一个对象属性里面去了
print(cs1.school.name)
print(cs1.school.studens)
print(cs1.teacher.age)
面向对象的三大特性
继承 多肽 封装
继承
class dad:
meony = 10
def __init__(self,name):
self.name =name
def dada(self):
print('打儿子')
class boby(dad): #子类继承了父类的数据和函数属性
meony = 100
pass
print(dad.__dict__)
print(boby.__dict__) #注意继承不是复制属性到子类中,子类没有那些
#属性和方法 在找不到数据属性,和函数属性再去
#父类中去查找
b1 = boby('alex') #实例化子类,子类没有实例函数,直接去父类中找
print(boby.__dict__) #父类需要一个参数,必须传一个参数。
b1.dada() #直接查子类无任何属性,因为没有就会去父类查找
#拿来用,间接的拥有父类属性
什么时候用继承
1.当类之间有显著不同,并且较小的类是较大的类所需的组件的时候,用组合比较好
2.当类之间有很多相同的功能,提取这些相同的功能做成基类,用继承比较好
继承的两种意义
1.继承基类,并且做出自己的改变或者扩展(子类有自己的属性方法(派生)) 这样减少代码重复
尽量少用大部分有害的,类与类耦合到一起,改变影响大,代码相对独立是好代码的一种体现
2.接口(方法)继承 父类规定的方法(接口)(不管方法有没有用) 子类继承后,必定都要间接有这两种方法(接口),自己再定义自己接口的程序 这样做的目的就是强制子类有父类的方法(接口),且子类自己还得定义方法的程序过程
import abc
class all_file(metaclass=abc.ABCMeta):
@abc.abstractclassmethod
def read(self):
print('必须有读接口方法')
@abc.abstractclassmethod
def write(self):
print('必须有写的接口方法')
class disk(all_file):
def read(self):
print('自身的读方法')
def write(self):
print('自身的写方法')
使用abc模块,就实现了只要继承它,就必须强制派生这两个方法,不能缺,不然报错 这样规范了子类,做了一个约束
python3 中都是新式类,默认继承object类 python2中是什么类要看类是否继承了object
深度优先和广度优先 广度优先会在最后基类时候不找,反过头去找另外一条线 深度一条线找到底 python2中经典类是深度优先 ,新式类都是广度优先
决定继承顺序的是MRO 这个元组 在类方法里面可以用__mro__来查看继承顺序
#子类调用父类属性方法,且自己又有所不同
class dad:
def __init__(self,name,age,work):
self.name=name
self.age=age
self.work=work
def daren(self):
print('%s打儿子'%self.name)
class boy(dad):
def __init__(self,name,age,work,count):
#dad.__init__(self,name,age,work) #继承的类数据属性有些不同,要重新定义
super().__init__(name,age,work) #自动帮你传self super()获取父类名
self.caobi = count #但是父类数据属性又全部要,用类方法
#调用__init__方法,得到数据属性
def daren(self): #想用父类方法,但是要改进一些,有些许差异
print('打的好')
#dad.daren(self)
super().daren()
#以上实现了需求 但是子类中用到父类的类名,如果父类类名改,就会连带修改子类
多肽
一种接口,多种实现
多肽是一种继承的细节体现过程
首先 1 不同子类继承父类 2 子类实例化 3不同实例去执行父类方法 多肽是基于继承上面的一种过程体现
#多肽
class H2O:
def __init__(self,feidian):
self.feidian=int(feidian)
def bian(self):
if self.feidian <= 0:
print("温度低于零摄氏度,水变成冰的状态")
elif self.feidian > 0 and self.feidian < 100:
print('温度处于零到一百之前,是出于水的状态')
else:
print('大于一百度,处于气化状态')
calss water:
pass
class water2:
pass
class water3:
pass
L1 = water(-1)
L2 = water2(30)
L3 = water3(120)
L1.bian() #不同的实例,调用同一个父类方法,
L2.bian() #比方在python中,我们使用len函数去测长度,其实不同
L3.bian() #数据类型继承了len类的方法,执行len方法的过程就是多肽
多肽是在你执行父类的方法时候体现出来的,这个过程就体现多肽
#封装
'''
类和实例就像一个口袋,把数据属性和函数属性装进去,至于封,就是外面调用不到
里面的东西'''
class people:
__bi = '奋斗'
_lou = '努力'
where = 'diqiu'
def __init__(self,name):
self.name = name
def fangfa(self):
print('方法')
def get__bi(self): #接口函数
print(self.__bi)
p1 =people('二哈')
print(p1.name) #调用者看不到调用函数执行过程,封装的体现之一
print(p1._lou) #在属性前面加一个下划线是python希望约定你外部别调用
#这样的属性,只是约定,你也可以强行调用
#print(p1.__bi) #调用不到,不是正真意义的封装,外部调用不到,是以双下划线
print(p1.__dict__)#开头的属性,执行的时候类都会重新命名,加上_类名 变量名
print(p1._people__bi) #强行调用__开头的属性
#封装是明确区分内外,外面无法直接调用内部的私有属性。内部可以调用内部的私有属性
但是封装要考虑好数据是否私有化,去隐藏。因为后期有可能会经常用到此属性
接口函数帮助用户去访问私有属性,就相当于在类中开了口子。设计好接口
继承,多肽,封装。是一种思想,只是python中的不一定非要用,滥用不一定代码高级。
#反射
class people:
def __init__(self,name,age):
self.name = name
self.age = age
def see(self):
print('see av paly')
P1 = people('张三',25)
hasattr(P1,'张三') #等于判断 P1.name是否存在
getattr(P1,'see','没有此变量显示此字符串') #等于 P1.see
setattr(P1,'age',26) #等于 P1.age = 26
delattr(P1,'age') #等于 del P1.age
为什么要用反射,如果一段别人的代码写到一半,有类,但是没写完耽搁了。如果我们直接去调用类里面的属性就会
报错,但是使用反射,就能先判断类属性的存在,再去获取类属性,执行相应操作。不存在则执行别的程序
#包装标准类型 #对已经存在的类进行定制化改变 以填补类方法的不足,满足自己的功能
class List(list): #自己定义一个列表子类
def append(self, p_object): #自己派生append方法 ,字符串才能追加
if type(p_object) is str:
super().append(p_object) #使用父类的基本的append方法,避免出现递归
else:
pass
#用到了继承和派生 ,这样我们有时候提供的标准类方法不够用的时候,可以自己定义一个经常用到发方法,方便以后使用
class Open:
def __init__(self,file,model='r',encoding='utf-8'): #后两个参数没有聚默认后面的值
self.file = open(file,model,encoding='utf-8') #这里是关键,把一个属性的值变成open打开的文件句柄,方便以后调用
self.model = model
self.Ecoding = encoding
def __getattr__(self, item):
#return self.file.item(self) #注意,在调用方法的时候,如果传的方法是字符串,不能直接调用,要用getattr去调
return getattr(self.file,item) #就相当于调模块,不能直接使用字符串去掉用
f = Open('jubing','w',encoding='utf-8')
f.write('dasdasd')
#继承 派生 去定制化标准类 叫做 包装
#用__getattr__去定制化标准类 叫做 授权 两者实现目的是一样的
动态导入
我们导入模块是直接写模块的名字(变量名),如果是字符串怎么导入模块(动态导入)
使用__import__()
ady = __import__('day4') #导入的字符对应的模块 附一个变量名存储
print(ady.people.where) #实用里面的东西
注意当时用__import__()的时候,如果是多层导入 即day4.day3.day2 它会找到day2模块解释一遍,但是停留在顶层
就是day4这个模块层,不会直接是day2模块层
使用 importlib
import importlib
l = importlib.import_module('day4.day3.day2')
l.test() #可以直接导入多层模块的想要的层数,比__import__要好用一点
如果导入的模块中有函数前面加了_,则跟class宗旨是一样的,不加_去调用或者*去调用都会找不到,用_去调用也违背了python约定
不要这样调用的约定。
#__getattr__ __delattr__ __setattr__
class foo:
def __init__(self,y):
self.name=y
def __getattr__(self, item):
print('%s的属性不存在'%item)
def __delattr__(self, item):
print('删除了%s的属性'%self.item)
def __setattr__(self, key, value):
print(以实例就相当于改变属性,在执行完__init__后执行此函数)
self.__dict__[key] = value #这里注意不能直接赋值,一改变属性
#又会执行setattr函数,会出现递归 如果setarr没有把属性加入属性字典,则__init__的属性将不会存储,为空
f = foo('cao') #实例化,就是改变属性,在执行完__init__后再执行__setattr__,这个函数默认去设置属性字典
print(f.name)
print(f.__dict__)
f.z #没有z属性,执行getattr
del f.name #删除操作执行delattr方法
这三个方法跟类其实没有什么关系,当用实例去调用属性的时候才触发
__都是内置的方法 ,这三个类的内置方法。系统已经定义好了。当我们去查找,修改,删除其实就是对应那三个内置的方法。其实主要
用还是用__getattr__,可以自己定义查找的方法,找不到属性不用出现报错

浙公网安备 33010602011771号