• day31


    今日内容概要

    • 组合

    • 反射(通过字符串来操作属性)

    • 魔术方法(内置方法,双下滑线开头的方法)

    • 异常(剩余一部分)

    • 元类

    组合

    在一个类中以另外一个类的对象作为数据属性,称为类的组合

          组合与继承都是用来解决代码的重用性问题

    不同的是:                                                                                                                                              继承是一种“是”的关系,比如老师是人、学生是人,当类之间有很多相同之处,应该使用继承;      而组合则是一种“有”的关系,比如老师有生日,老师有很多门课程,当类之间有显著不同,并且较小的类是较大的类所需要的组件时,应该使用组合

    如下实例

     

    1. class Course:
    2. def __init__(self,name,period,price):
    3. self.name=name
    4. self.period=period
    5. self.price=price
    6. def tell_info(self):
    7. print('<%s %s %s>' %(self.name,self.period,self.price))
    8. class Date:
    9. def __init__(self,year,mon,day):
    10. self.year=year
    11. self.mon=mon
    12. self.day=day
    13. def tell_birth(self):
    14. print('<%s-%s-%s>' %(self.year,self.mon,self.day))
    15. class People:
    16. school='清华大学'
    17. def __init__(self,name,sex,age):
    18. self.name=name
    19. self.sex=sex
    20. self.age=age
    21. #Teacher类基于继承来重用People的代码,基于组合来重用Date类和Course类的代码
    22. class Teacher(People): #老师是人
    23. def __init__(self,name,sex,age,title,year,mon,day):
    24. super().__init__(name,age,sex)
    25. self.birth=Date(year,mon,day) #老师有生日
    26. self.courses=[] #老师有课程,可以在实例化后,往该列表中添加Course类的对象
    27. def teach(self):
    28. print('%s is teaching' %self.name)
    29. python=Course('python','3mons',3000.0)
    30. linux=Course('linux','5mons',5000.0)
    31. teacher1=Teacher('lili','female',28,'博士生导师',1990,3,23)
    32. # teacher1有两门课程
    33. teacher1.courses.append(python)
    34. teacher1.courses.append(linux)
    35. # 重用Date类的功能
    36. teacher1.birth.tell_birth()
    37. # 重用Course类的功能
    38. for obj in teacher1.courses:
    39. obj.tell_info()

    此时对象teacher1集对象独有的属性、Teacher类中的内容、Course类中的内容于一身(都可以访问到),是一个高度整合的产物

    反射

    在python中,反射指的是通过字符串来操作对象的属性,涉及到四个内置函数的使用(python中一切皆对象都可以用下述四个方法)

    1. class Teacher:
    2. def __init__(self,full_name):
    3. self.full_name =full_name
    4. t=Teacher('Egon Lin')
    5. # hasattr(object,'name')
    6. hasattr(t,'full_name') # 按字符串'full_name'判断有无属性t.full_name
    7. # getattr(object, 'name', default=None)
    8. getattr(t,'full_name',None) # 等同于t.full_name,不存在该属性则返回默认值None
    9. # setattr(x, 'y', v)
    10. setattr(t,'age',18) # 等同于t.age=18
    11. # delattr(x, 'y')
    12. delattr(t,'age') # 等同于del t.age

    基于反射可以十分灵活地操作对象的属性,比如将用户交互的结果反射到具体的功能执行

    1. >>> class FtpServer:
    2. ... def serve_forever(self):
    3. ... while True:
    4. ... inp=input('input your cmd>>: ').strip()
    5. ... cmd,file=inp.split()
    6. ... if hasattr(self,cmd): # 根据用户输入的cmd,判断对象self有无对应的方法属性
    7. ... func=getattr(self,cmd) # 根据字符串cmd,获取对象self对应的方法属性
    8. ... func(file)
    9. ... def get(self,file):
    10. ... print('Downloading %s...' %file)
    11. ... def put(self,file):
    12. ... print('Uploading %s...' %file)
    13. ...
    14. >>> server=FtpServer()
    15. >>> server.serve_forever()
    16. input your cmd>>: get a.txt
    17. Downloading a.txt...
    18. input your cmd>>: put a.txt
    19. Uploading a.txt...

    内置方法

    Python的Class机制内置了很多特殊的方法来帮助使用者高度定制自己的类,这些内置方法都是以双下划线开头和结尾的,会在满足某种条件时自动触发

    我们以常用的__str__和__del__来介绍它们的使用

    __str__,__repr__方法

    __str__方法会在对象被打印时自动触发,print功能打印的就是它的返回值,我们通常基于方法来对象的打印信息,该方法必须返回字符串类型

    1. >>> class People:
    2. ... def __init__(self,name,age):
    3. ... self.name=name
    4. ... self.age=age
    5. ... def __str__(self):
    6. ... return '' %(self.name,self.age) #返回类型必须是字符串
    7. ...
    8. >>> p=People('lili',18)
    9. >>> print(p) #触发p.__str__(),拿到返回值后进行打印
    10. 18>
    11. class School:
    12. def __init__(self,name,addr,type):
    13. self.name=name
    14. self.addr=addr
    15. self.type=type
    16. def __repr__(self):
    17. return 'School(%s,%s)' %(self.name,self.addr)
    18. def __str__(self):
    19. return '(%s,%s)' %(self.name,self.addr)
    20. s1=School('oldboy1','北京','私立')
    21. print('from repr: ',repr(s1))
    22. print('from str: ',str(s1))
    23. print(s1)

    str函数或者print函数--->obj.__str__()

    repr或者交互式解释器--->obj.__repr__()

    如果__str__没有被定义,那么就会使用__repr__来代替输出

    注意:这俩方法的返回值必须是字符串,否则抛出异常

    __del__方法

    __del__会在对象被删除时自动触发。

       由于Python自带的垃圾回收机制会自动清理Python程序的资源,所以当一个对象只占用应用程序级资源时,完全没必要为对象定制__del__方法,但在产生一个对象的同时涉及到申请系统资源(比如系统打开的文件、网络连接等)的情况下,关于系统资源的回收,Python的垃圾回收机制便派不上用场了,需要我们为对象定制该方法,用来在对象被删除时自动删除时自动触发回收系统资源的操作

    1. class MySQL:
    2. def __init__(self,ip,port):
    3. self.conn=connect(ip,port) # 伪代码,发起网络连接,需要占用系统资源
    4. def __del__(self):
    5. self.conn.close() # 关闭网络连接,回收系统资源
    6. obj=MySQL('127.0.0.1',3306) # 在对象obj被删除时,自动触发obj.__del__()

    isinstance(obj,cls)和issubclass(sub,super)

    isinstance(obj,cls)检查是否obj是否是类cls的对象

    1. class Foo(object):
    2. 2 pass
    3. 3
    4. 4 obj = Foo()
    5. 5
    6. 6 isinstance(obj, Foo)

    issubclass(sub,super)检查sub类是否是super类的派生类

    1. class Foo(object):
    2. pass
    3. class Bar(Foo):
    4. pass
    5. issubclass(Bar, Foo)

    __doc__

    1. class Foo:
    2. '我是描述信息'
    3. pass
    4. print(Foo.__doc__)
    1. class Foo:
    2. '我是描述信息'
    3. pass
    4. class Bar(Foo):
    5. pass
    6. print(Bar.__doc__) #该属性无法继承给子类

    __enter__和__exit__

    1. # 我们知道在操作文件对象的时候可以这么写
    2. with open('a.txt') as f:
    3.   '代码块'

    上述叫做上下文管理协议,即with语句,为了让一个对象兼容with语句,必须在这个对象的类中声明__enter__和__exit__方法

    1. class Open:
    2. def __init__(self,name):
    3. self.name=name
    4. def __enter__(self):
    5. print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
    6. # return self
    7. def __exit__(self, exc_type, exc_val, exc_tb):
    8. print('with中代码块执行完毕时执行我啊')
    9. with Open('a.txt') as f:
    10. print('=====>执行代码块')
    11. # print(f,f.name)

    __exit__()中的三个参数分别代表:异常类型  异常值  追溯信息

    with语句中代码块出现异常,则with后的代码都无法执行

    1. class Open:
    2. def __init__(self,name):
    3. self.name=name
    4. def __enter__(self):
    5. print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
    6. def __exit__(self, exc_type, exc_val, exc_tb):
    7. print('with中代码块执行完毕时执行我啊')
    8. print(exc_type)
    9. print(exc_val)
    10. print(exc_tb)
    11. with Open('a.txt') as f:
    12. print('=====>执行代码块')
    13. raise AttributeError('***着火啦,救火啊***')
    14. print('0'*100) #------------------------------->不会执行

    如果__exit__()返回值为True,那么异常会被清空,就好像啥都没发生一样,with后的语句正常执行

    1. class Open:
    2. def __init__(self,name):
    3. self.name=name
    4. def __enter__(self):
    5. print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
    6. def __exit__(self, exc_type, exc_val, exc_tb):
    7. print('with中代码块执行完毕时执行我啊')
    8. print(exc_type)
    9. print(exc_val)
    10. print(exc_tb)
    11. return True
    12. with Open('a.txt') as f:
    13. print('=====>执行代码块')
    14. raise AttributeError('***着火啦,救火啊***')
    15. print('0'*100) #------------------------------->会执行

    __setattr__,__delattr__,__getattr__

     

    1. class Foo:
    2. x=1
    3. def __init__(self,y):
    4. self.y=y
    5. def __getattr__(self, item):
    6. print('----> from getattr:你找的属性不存在')
    7. def __setattr__(self, key, value):
    8. print('----> from setattr')
    9. # self.key=value #这就无限递归了,你好好想想
    10. # self.__dict__[key]=value #应该使用它
    11. def __delattr__(self, item):
    12. print('----> from delattr')
    13. # del self.item #无限递归了
    14. self.__dict__.pop(item)
    15. #__setattr__添加/修改属性会触发它的执行
    16. f1=Foo(10)
    17. print(f1.__dict__) # 因为你重写了__setattr__,凡是赋值操作都会触发它的运行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值
    18. f1.z=3
    19. print(f1.__dict__)
    20. #__delattr__删除属性的时候会触发
    21. f1.__dict__['a']=3#我们可以直接修改属性字典,来完成添加/修改属性的操作
    22. del f1.a
    23. print(f1.__dict__)
    24. #__getattr__只有在使用点调用属性且属性不存在的时候才会触发
    25. f1.xxxxxx

    __setitem__,__getitem__,__delitem__

    1. class Foo:
    2. def __init__(self,name):
    3. self.name=name
    4. def __getitem__(self, item):
    5. print(self.__dict__[item])
    6. def __setitem__(self, key, value):
    7. self.__dict__[key]=value
    8. def __delitem__(self, key):
    9. print('del obj[key]时,我执行')
    10. self.__dict__.pop(key)
    11. def __delattr__(self, item):
    12. print('del obj.key时,我执行')
    13. self.__dict__.pop(item)
    14. f1=Foo('sb')
    15. f1['age']=18
    16. f1['age1']=19
    17. del f1.age1
    18. del f1['age']
    19. f1['name']='alex'
    20. print(f1.__dict__)

    __call__

    对象后面加括号,触发执行

    构造方法的执行是由创建对象触发的 即:对象=类名;而对__call__方法的执行是由对象后加括号触发的 即:对象()或者类()()

    1. class Foo:
    2. def __init__(self):
    3. pass
    4. def __call__(self, *args, **kwargs):
    5. print('__call__')
    6. obj = Foo() # 执行 __init__
    7. obj() # 执行 __call__

  • 相关阅读:
    SCConv:用于特征冗余的空间和通道重构卷积
    关于时空数据的培训 GAN:实用指南(第 02/3 部分)
    JAVA基础
    OS2.3.6:生产者,消费者问题
    我的创作纪念日
    软件系统分析与设计(软件设计师笔记)
    Python中将字典转为成员变量
    【ELK使用指南 2】常用的 Logstash filter 插件详解(附应用实例)
    【C++学习第八讲】简单变量(二)
    业务安全五重价值:防攻击、保稳定、助增收、促合规、提升满意度
  • 原文地址:https://blog.csdn.net/qq_67257222/article/details/133746596