抽象指对现实世界问题和实体的本质表现,行为和特征建模,建立一个相关的子集,可以用于 绘程序结构,从而实现这种模型。抽象不仅包括这种模型的数据属性,还定义了这些数据的接口。
对某种抽象的实现就是对此数据及与之相关接口的现实化(realization)。现实化这个过程对于客户 程序应当是透明而且无关的。
封装/接口
封装描述了对数据/信息进行隐藏的观念,它对数据属性提供接口和访问函数。通过任何客户端直接对数据的访问,无视接口,与封装性都是背道而驰的,除非程序员允许这些操作。作为实现的 一部分,客户端根本就不需要知道在封装之后,数据属性是如何组织的。在Python中,所有的类属性都是公开的,但名字可能被“混淆”了,以阻止未经授权的访问,但仅此而已,再没有其他预防措施了。这就需要在设计时,对数据提供相应的接口,以免客户程序通过不规范的操作来存取封装的数据属性。
注意:封装绝不是等于“把不想让别人看到、以后可能修改的东西用private隐藏起来”
真正的封装是,经过深入的思考,做出良好的抽象,给出“完整且最小”的接口,并使得内部细节可以对外透明
(注意:对外透明的意思是,外部调用者可以顺利的得到自己想要的任何功能,完全意识不到内部细节的存在)
合成
合成扩充了对类的 述,使得多个不同的类合成为一个大的类,来解决现实问题。合成 述了 一个异常复杂的系统,比如一个类由其它类组成,更小的组件也可能是其它的类,数据属性及行为, 所有这些合在一起,彼此是“有一个”的关系。
派生/继承/继承结构
派生描述了子类衍生出新的特性,新类保留已存类类型中所有需要的数据和行为,但允许修改或者其它的自定义操作,都不会修改原类的定义。
继承描述了子类属性从祖先类继承这样一种方式
继承结构表示多“代”派生,可以述成一个“族谱”,连续的子类,与祖先类都有关系。
泛化/特化
基于继承
泛化表示所有子类与其父类及祖先类有一样的特点。
特化描述所有子类的自定义,也就是,什么属性让它与其祖先类不同。
多态与多态性
多态指的是同一种事物的多种状态:水这种事物有多种不同的状态:冰,水蒸气
多态性的概念指出了对象如何通过他们共同的属性和动作来操作及访问,而不需考虑他们具体的类。
冰,水蒸气,都继承于水,它们都有一个同名的方法就是变成云,但是冰.变云(),与水蒸气.变云()是截然不同的过程,虽然调用的方法都一样
自省/反射
自省也称作反射,这个性质展示了某对象是如何在运行期取得自身信息的。如果传一个对象给你,你可以查出它有什么能力,这是一项强大的特性。如果Python不支持某种形式的自省功能,dir和type内建函数,将很难正常工作。还有那些特殊属性,像__dict__,__name__及__doc__
反射的概念由smith在1982年首次提出的,主要是指程序可以访问、检测、和修改它本身状态或行为的一种能力(自省),这一概念的提出很快引发了计算机科学领域关于应用反射的研究,它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得成绩
四个可以实现自省的函数
下面的方法适用于类和对象
hasattr(object,name) #判断对象或类里到底有没有 name 这个属性 ,相当于 object.name in class.__dict__
getattr(object,name,default=None)
setattr(object,name,value)
delattr(object,name)
classVehicle:def __init__(self,name,speed,load,power):
self.name=name
self.speed=speed
self.load=load
self.power=powerdefrun(self):print("开动了")
v1= Vehicle('sad',1212,1212,'asd')print(hasattr(v1,"name")) #----> True
print(hasattr(v1,"sada")) #----> False
print(getattr(v1,"run")) #> 函数地址
getattr(v1,"run")() #输出 开动了 相当于执行run函数#getattr(v1,"dsadas") #报错,如果没有这个属性,而且没有传入default默认值,就会报错
print(getattr(v1,"dasdsad","没有这个函数")) #输出没有这个函数
setattr(v1,'age',12)print(v1.__dict__) #给v1添加一个age的数据属性,值为12
setattr(v1,'run1',lambda self:self.age + 10)print(v1.__dict__) #给v1添加一个run1的函数属性,
print(v1.run1(v1))
delattr(v1,"run1") #删除run1这个函数属性
print(v1.__dict__)
结果:
True
False
>
开动了
没有这个函数
{'name': 'sad', 'speed': 1212, 'load': 1212, 'power': 'asd', 'age': 12}
{'name': 'sad', 'speed': 1212, 'load': 1212, 'power': 'asd', 'age': 12, 'run1': at 0x00000000001D1E18>}
22
{'name': 'sad', 'speed': 1212, 'load': 1212, 'power': 'asd', 'age': 12}
为什么要用反射?
反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种“后期绑定”,就是你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能
动态导入模块
__import__(str(模块名))
m = __import__('m1.t') #导入的只取最顶层,这里也就是m1,后面的t模块不会导入
importlib.import_module('m1.s16') #这个会导入整个字符串里面的模块,这里是m1目录下面的s16模块
importimportlib
m= importlib.import_module('m1.s16')print(m)
m.test()
结果test
类的内置双下划线attr属性
__getattr__(self,item) 当对象调用一个不存在的属性是会自动触发这个函数
__delattr__(self,item) 当删除对象属性的时候会触发这个函数
__setattr__(self,key,value) 设置对象属性的时候会执行这个函数,如self.f = 1 这个就会触发,注意,如果重写了这个函数,必须要在函数里面用最底层的操作给属性赋值: self.__dict__[key] = value ,否则用self.key = value来设置,又会重新调用自己,形成没有结束的递归
classtest:def __init__(self,name):
self.name=namedef __getattr__(self, item):print("%s 这个属性不存在" %item)def __setattr__(self, key, value):if type(value) isstr:print("开始设置",key,value )#self.key = value #这样会报错
self.__dict__[key] = value #这里如果不写,那么所有的属性操作(赋值、修改)等、都不会执行,查询print(__dict__)会是一个空字典
else:print("必须是字符串类型")#print("执行setattr ",key,value )
def __delattr__(self, item):print('执行delattr',item)
self.__dict__.pop(item) #这里如果不写,那么删除操作都不会真正删除,查询print(__dict__)还会是在字典里面
t1= test('alex')
t1.age
t1.age= "19"
print(t1.__dict__)delt1.ageprint(t1.__dict__)
结果:
开始设置 name alex
age 这个属性不存在
开始设置 age19{'name': 'alex', 'age': '19'}
执行delattr age
{'name': 'alex'}
二次加工标准类型(包装)
包装:python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们学过的继承/派生知识(其他的标准类型可以通过下面的方式进行二次加工)
classList(list):defappend(self, object):if type(object) isstr:
super().append(object)else:print("只能添加字符")defshow_midlle(self):return self[len(self)//2]
l= List('hello worldw')print(l)
l.append('s')print(l)print(l.show_midlle())
l.append(1)print(l)
结果:
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 'w']
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 'w', 's']
w
只能添加字符
['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', 'w', 's']
授权:授权是包装的一个特性,包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能,其他的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性
#授权给Open函数文件操作,同时重写write函数,给写进去的字符串前面都加上时间,相当于给write新添加了格式功能
importtimeclassOpen:def __init__(self,filename,modu='r',encode='utf-8'):
self.file= open(filename, modu,encoding=encode)def __getattr__(self, item):returngetattr(self.file,item)defwrite(self,string):
self.file.write('%s %s \n'%(time.asctime(),string))
f= Open('s18.py','a+')#print(f.file)#print(f.read())
f.write('sd')
f.seek(0)print(f.read())