Python-28_组合_继承_多态_封装_反射
# class Hand: # pass # class Foot: # pass # class Trunk: # pass # class Head: # pass # # class Person: # def __init__(self,id_num,name): # self.id_num=id_num # self.name=name # self.hand=Hand() # self.foot=Foot() # self.trunk=Trunk() # self.head=Head() # p1=Person("1","newmet") # print(p1.__dict__) class School: def __init__(self, name, addr): self.name = name self.addr = addr class Course: def __init__(self, name, price, period, school): self.name = name self.price = price self.period = period self.school = school s1 = School("python", "上海") s2 = School("python", "北京") s3 = School("python", "广州") # c1=Course("python","100","newmet",s1) # print(c1.school.name) # python msg = """ 1 python 上海校区 2 python 北京校区 3 python 广州校区 """ while True: print(msg) menu = {"1":s1,"2":s2,"3":s3} choice = input("选择学校》》:") school_obj=menu[choice] name = input("课程名》》:") price = input("课程费用》》:") period = input("课程周期》》:") new_course=Course(name,price,period,school_obj) print("课程【%s】属于【%s】学校"%(new_course.name,new_course.school.name)) # 组合 ------ 用于将类与类之间联结
class Dad: "爸爸类" money=10 def __init__(self,name): print("爸爸") self.name=name def hit_son(self): print("%s正在打儿子" %self.name) class Son(Dad): "儿子类" pass # print(Son.money) # Son.hit_son() # print(Dad.__dict__) # print(Son.__dict__) s1=Son("newmet") # 爸爸 print(s1.name) s1.hit_son()
# 接口 就是一个函数 # 接口继承 --- 定义一个基类,在基类中将自己的方法定义成接口函数,子类继承该基类时-必须实现基类中的方法 # 接口函数不必实现,只是为了规范子类。 #------------------- 接口继承-------------------------- import abc class All_file(metaclass=abc.ABCMeta): @abc.abstractclassmethod # 抽象的方法,下面的方法不用具体实现 def read(self): pass @abc.abstractclassmethod def write(self): pass class Sata(All_file): def read(self): print("Sata read") def write(self): print("Sata write") class Cdrom(All_file): def read(self): print("Cdrom read") def write(self): print("Cdrom write") class Mem(All_file): def read(self): print("Mem read") def write(self): print("Mem write") m1=Mem() m1.read() m1.write() # 接口继承实质上是要求“做出一个良好地抽象,这个抽象规定了一个兼容接口,使得外部调用者无需关心具体细节, # 可一视同仁的处理实现了特定接口的所有对象”----这在程序设计上,叫做归一化。 # 归一化使得高层的外部使用者可以不加区分的处理所有接口兼容的对象集合。
#---------- 基本方法,但是不利于后续代码的修改-------------- # class School: # def __init__(self,name,area): # self.name=name # self.area=area # def Ranking(self): # print("排名第一") # class Class_G(School): # def __init__(self,name,area,grade): # School.__init__(self,name,area) # ===父类中self.name=name self.area=area # self.grade=grade # def Title(self): # School.Ranking(self) # print("%s 班最强"%self.grade) # # grade1=Class_G("大学","1000","A") # grade1.Title() #------------------------super().__init__ ----------------------------- class School: def __init__(self,name,area): self.name=name self.area=area def Ranking(self): print("排名第一") class Class_G(School): def __init__(self,name,area,grade): super().__init__(name,area) # ===父类中self.name=name self.area=area self.grade=grade def Title(self): super().Ranking() print("%s 班最强"%self.grade) grade1=Class_G("大学","1000","A") grade1.Title()
import pickle class School: def __init__(self, name, addr): self.name = name self.addr = addr def save(self): with open("school.db","wb") as f: pickle.dump(self,f) class Course: def __init__(self, name, price, period, school): self.name = name self.price = price self.period = period self.school = school school_obj=pickle.load(open("school.db","rb")) print(school_obj.name,school_obj.addr) # s1=School("NewMet1","北京") # s2=School("NewMet2","上海") # s1.save() # s2.save()
# 多态指的是一类事物有多种形态 ############################动物有多种形态:人,狗,猪 import abc class Animal(metaclass=abc.ABCMeta): #同一类事物:动物 @abc.abstractmethod def talk(self): pass class People(Animal): #动物的形态之一:人 def talk(self): print('say hello') class Dog(Animal): #动物的形态之二:狗 def talk(self): print('say wangwang') class Pig(Animal): #动物的形态之三:猪 def talk(self): print('say aoao') #######################文件有多种形态:文本文件,可执行文件 import abc class File(metaclass=abc.ABCMeta): #同一类事物:文件 @abc.abstractmethod def click(self): pass class Text(File): #文件的形态之一:文本文件 def click(self): print('open file') class ExeFile(File): #文件的形态之二:可执行文件 def click(self): print('execute file') """ 在面向对象方法中一般是这样表述多态性:向不同的对象发送同一条消息(!!!obj.func():是调用了obj的方法func, 又称为向obj发送了一条消息func),不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自 己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。 比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作, 虽然二者消息一样,但是执行的效果不同 """ peo=People() dog=Dog() pig=Pig() #peo、dog、pig都是动物,只要是动物肯定有talk方法 #于是我们可以不用考虑它们三者的具体是什么类型,而直接使用 peo.talk() dog.talk() pig.talk() #更进一步,我们可以定义一个统一的接口来使用 def func(obj): obj.talk() """ 1.增加了程序的灵活性 2.增加了程序额可扩展性 """ #str,list,tuple都是序列类型 s=str('hello') l=list([1,2,3]) t=tuple((4,5,6)) #我们可以在不考虑三者类型的前提下使用s,l,t s.__len__() l.__len__() t.__len__() len(s) len(l) len(t)
#其实这仅仅这是一种变形操作且仅仅只在类定义阶段发生变形 #类中所有双下划线开头的名称如__x都会在类定义时自动变形成:_类名__x的形式: class A: __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N def __init__(self): self.__X=10 #变形为self._A__X def __foo(self): #变形为_A__foo print('from A') def bar(self): self.__foo() #只有在类内部才可以通过__foo的形式访问到. #A._A__N是可以访问到的, #这种,在外部是无法通过__x这个名字访问到。 """ 1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性, 然后就可以访问了,如a._A__N,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形, 主要用来限制外部的直接访问。 2.变形的过程只在类的定义时发生一次,在定义后的赋值操作,不会变形 3.在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的 """ # 正常情况 class A: def fa(self): print("from A") def test(self): self.fa() class B(A): def fa(self): print("from B") b=B() b.test() # from B # 把fa定义成私有的,即 __fa class A: def __fa(self): print("from A") def test(self): self.__fa() class B(A): def __fa(self): print("from B") b = B() b.test() # from A """ 封装的真谛在于明确地区分内外,封装的属性可以直接在内部使用,而不能被外部直接使用,然而定义属性的目的终归是要用, 外部要想用类隐藏的属性,需要我们为其开辟接口,让外部能够间接地用到我们隐藏起来的属性,那这么做的意义何在??? 1:封装数据:将数据隐藏起来这不是目的。隐藏起来然后对外提供操作该数据的接口,然后我们可以在接口附加上对该数据 操作的限制,以此完成对数据属性操作的严格控制。 class Teacher: def __init__(self,name,age): # self.__name=name # self.__age=age self.set_info(name,age) def tell_info(self): print('姓名:%s,年龄:%s' %(self.__name,self.__age)) def set_info(self,name,age): if not isinstance(name,str): raise TypeError('姓名必须是字符串类型') if not isinstance(age,int): raise TypeError('年龄必须是整型') self.__name=name self.__age=age t=Teacher('egon',18) t.tell_info() t.set_info('egon',19) t.tell_info() 2:封装方法:目的是隔离复杂度 class ATM: def __card(self): print('插卡') def __auth(self): print('用户认证') def __input(self): print('输入取款金额') def __print_bill(self): print('打印账单') def __take_money(self): print('取款') def withdraw(self): self.__card() self.__auth() self.__input() self.__print_bill() self.__take_money() a=ATM() a.withdraw() 在编程语言里,对外提供的接口(接口可理解为了一个入口),可以是函数,称为接口函数, 这与接口的概念还不一样,接口代表一组接口函数的集合体。 """
# 1 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省) # 2 python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射) q=1 # 乱写的 ##################### 一、四个可以实现自省的函数 ################## """ 适用于类和对象(一切皆对象,类本身也是一个对象) 四个可以实现自省的函数 hasattr(object,name) getattr(object, name, default=None) setattr(x, y, v) delattr(x, y) """ class BlackMedium: feature='Ugly' def __init__(self,name,addr): self.name=name self.addr=addr def sell_house(self): print('%s 黑中介卖房子啦,傻逼才买呢,但是谁能证明自己不傻逼' %self.name) def rent_house(self): print('%s 黑中介租房子啦,傻逼才租呢' %self.name) b1=BlackMedium('万成置地','回龙观天露园') #检测是否含有某属性 print(hasattr(b1,'name')) print(hasattr(b1,'sell_house')) #获取属性 n=getattr(b1,'name') print(n) func=getattr(b1,'rent_house') func() # getattr(b1,'aaaaaaaa') #报错 print(getattr(b1,'aaaaaaaa','不存在啊')) #设置属性 setattr(b1,'sb',True) setattr(b1,'show_name',lambda self:self.name+'sb') print(b1.__dict__) print(b1.show_name(b1)) #删除属性 delattr(b1,'addr') delattr(b1,'show_name') delattr(b1,'show_name111')#不存在,则报错 print(b1.__dict__) ##################### 二、反射的好处 ################## """ 好处一:实现可插拔机制 可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思? 即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能. """ "未完成的接口功能" class FtpClient: 'ftp客户端,但是还么有实现具体的功能' def __init__(self,addr): print('正在连接服务器[%s]' %addr) self.addr=addr "上述接口完成与否,不影响正常代码的编写" #from module import FtpClient f1=FtpClient('192.168.1.1') if hasattr(f1,'get'): # 检测是否含有某属性 func_get=getattr(f1,'get') # 获取属性 func_get() else: print('---->不存在此方法') print('处理其他的逻辑') """ 好处二:动态导入模块(基于反射当前模块成员) sys.modules[__name__] #调取当前所在的模块 """
# 一、常规导入模块 # from m1 import t # 二、通过字符串的形式导入模块 module_t=__import__("m1.t") # 获取顶级的模块名-- m1 module_t.t.test1() # test1 # 三、导入模块的形式调用调用模块 import importlib m=importlib.import_module("m1.t") print(m) # <module 'm1.t' from 'E:\\Python+AI\\newmet\\09_组合_继承_多态_封装_反射\\m1\\t.py'> m.test2() # test2
q=1 """ __setattr__, __delattr__, __getattr__ """ class Foo: x=1 def __init__(self,y): self.y=y def __getattr__(self, item): print('----> from getattr:你找的属性不存在') def __setattr__(self, key, value): print('----> from setattr') # self.key=value #这就无限递归了,你好好想想 # self.__dict__[key]=value #应该使用它 def __delattr__(self, item): print('----> from delattr') # del self.item #无限递归了 self.__dict__.pop(item) #__setattr__添加/修改属性会触发它的执行 f1=Foo(10) print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值 f1.z=3 print(f1.__dict__) #__delattr__删除属性的时候会触发 f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作 del f1.a print(f1.__dict__) #__getattr__只有在使用点调用属性且属性不存在的时候才会触发 f1.xxxxxx
# 二次加工标准类型(基于继承实现) # 包装:python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的 # 数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工) """ class List(list): #继承list所有的属性,也可以派生出自己新的,比如append和mid def append(self, p_object): ' 派生自己的append:加上类型检查' if type(p_object) is str: super().append(p_object) else: print("只能添加字符串类型") def mid(self): '新增自己的属性' index=int(len(self)/2) return self[index] l=List("hello world") print(l) l.append(5) print(l) # l.append('1111111') #报错,必须为int类型 print(l.mid) #其余的方法都继承list的 l.insert(0,-123) print(l) l.clear() print(l) """ # clear加权限限制 class List(list): def __init__(self,item,tag=False): super().__init__(item) self.tag=tag def append(self, p_object): if not isinstance(p_object,str): raise TypeError super().append(p_object) def clear(self): if not self.tag: raise PermissionError super().clear() l=List([1,2,3],False) print(l) print(l.tag) l.append('saf') print(l) # l.clear() #异常 l.tag=True l.clear()
# 授权:授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。 # 其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。 # 实现授权的关键点就是覆盖__getattr__方法 # 示例一: import time class FileHandle: def __init__(self,filename,mode='r',encoding='utf-8'): self.file=open(filename,mode,encoding=encoding) def write(self,line): t=time.strftime('%Y-%m-%d %T') self.file.write('%s %s' %(t,line)) def __getattr__(self, item): return getattr(self.file,item) f1=FileHandle('b.txt','w+') f1.write('你好啊') f1.seek(0) print(f1.read()) f1.close() # 示例二: #_*_coding:utf-8_*_ __author__ = 'Linhaifeng' #我们来加上b模式支持 import time class FileHandle: def __init__(self,filename,mode='r',encoding='utf-8'): if 'b' in mode: self.file=open(filename,mode) else: self.file=open(filename,mode,encoding=encoding) self.filename=filename self.mode=mode self.encoding=encoding def write(self,line): if 'b' in self.mode: if not isinstance(line,bytes): raise TypeError('must be bytes') self.file.write(line) def __getattr__(self, item): return getattr(self.file,item) def __str__(self): if 'b' in self.mode: res="<_io.BufferedReader name='%s'>" %self.filename else: res="<_io.TextIOWrapper name='%s' mode='%s' encoding='%s'>" %(self.filename,self.mode,self.encoding) return res f1=FileHandle('b.txt','wb') # f1.write('你好啊啊啊啊啊') #自定制的write,不用在进行encode转成二进制去写了,简单,大气 f1.write('你好啊'.encode('utf-8')) print(f1) f1.close() #练习一 class List: def __init__(self,seq): self.seq=seq def append(self, p_object): ' 派生自己的append加上类型检查,覆盖原有的append' if not isinstance(p_object,int): raise TypeError('must be int') self.seq.append(p_object) @property def mid(self): '新增自己的方法' index=len(self.seq)//2 return self.seq[index] def __getattr__(self, item): return getattr(self.seq,item) def __str__(self): return str(self.seq) l=List([1,2,3]) print(l) l.append(4) print(l) # l.append('3333333') #报错,必须为int类型 print(l.mid) #基于授权,获得insert方法 l.insert(0,-123) print(l) #练习二 class List: def __init__(self,seq,permission=False): self.seq=seq self.permission=permission def clear(self): if not self.permission: raise PermissionError('not allow the operation') self.seq.clear() def __getattr__(self, item): return getattr(self.seq,item) def __str__(self): return str(self.seq) l=List([1,2,3]) # l.clear() #此时没有权限,抛出异常 l.permission=True print(l) l.clear() print(l) #基于授权,获得insert方法 l.insert(0,-123) print(l)