【六】面向对象之反射、内置方法
【一】反射
- 在Python中,反射指的是通过字符串来操作对象的属性
- 涉及到四个内置函数的使用(Python中一切皆对象,类和对象都可以用下述四个方法)
class Teacher:
def __init__(self,full_name):
self.full_name =full_name
t=Teacher('Egon Lin')
# hasattr(object,'name')
hasattr(t,'full_name') # 按字符串'full_name'判断有无属性t.full_name
# getattr(object, 'name', default=None)
getattr(t,'full_name',None) # 等同于t.full_name,不存在该属性则返回默认值None
# setattr(x, 'y', v)
setattr(t,'age',18) # 等同于t.age=18
# delattr(x, 'y')
delattr(t,'age') # 等同于del t.age
- 基于反射可以十分灵活地操作对象的属性
- 比如将用户交互的结果反射到具体的功能执行
>>> class FtpServer:
... def serve_forever(self):
... while True:
... inp=input('input your cmd>>: ').strip()
... cmd,file=inp.split()
... if hasattr(self,cmd): # 根据用户输入的cmd,判断对象self有无对应的方法属性
... func=getattr(self,cmd) # 根据字符串cmd,获取对象self对应的方法属性
... func(file)
... def get(self,file):
... print('Downloading %s...' %file)
... def put(self,file):
... print('Uploading %s...' %file)
...
>>> server=FtpServer()
>>> server.serve_forever()
input your cmd>>: get a.txt
Downloading a.txt...
input your cmd>>: put a.txt
Uploading a.txt...
【二】内置方法
- Python的Class机制内置了很多特殊的方法来帮助使用者高度定制自己的类
- 这些内置方法都是以双下划线开头和结尾的,会在满足某种条件时自动触发
- 我们以常用的
__str__
和__del__
为例来简单介绍它们的使用。
(1)__str__
,__repr__
方法
__str__
方法会在对象被打印时自动触发- print功能打印的就是它的返回值
- 我们通常基于方法来定制对象的打印信息
- 该方法必须返回字符串类型
>>> class People:
... def __init__(self,name,age):
... self.name=name
... self.age=age
... def __str__(self):
... return '<Name:%s Age:%s>' %(self.name,self.age) #返回类型必须是字符串
...
>>> p=People('lili',18)
>>> print(p) #触发p.__str__(),拿到返回值后进行打印
<Name:lili Age:18>
class School:
def __init__(self,name,addr,type):
self.name=name
self.addr=addr
self.type=type
def __repr__(self):
return 'School(%s,%s)' %(self.name,self.addr)
def __str__(self):
return '(%s,%s)' %(self.name,self.addr)
s1=School('oldboy1','北京','私立')
print('from repr: ',repr(s1))
print('from str: ',str(s1))
print(s1)
'''
str函数或者print函数--->obj.__str__()
repr或者交互式解释器--->obj.__repr__()
如果__str__
没有被定义,那么就会使用__repr__
来代替输出
注意:这俩方法的返回值必须是字符串,否则抛出异常
'''
(2)__del__
方法
__del__
会在对象被删除时自动触发。- 由于Python自带的垃圾回收机制会自动清理Python程序的资源,所以当一个对象只占用应用程序级资源时,完全没必要为对象定制
__del__
方法 - 但在产生一个对象的同时涉及到申请系统资源(比如系统打开的文件、网络连接等)的情况下
- 关于系统资源的回收,Python的垃圾回收机制便派不上用场了
- 需要我们为对象定制该方法,用来在对象被删除时自动触发回收系统资源的操作
- 由于Python自带的垃圾回收机制会自动清理Python程序的资源,所以当一个对象只占用应用程序级资源时,完全没必要为对象定制
class MySQL:
def __init__(self,ip,port):
self.conn=connect(ip,port) # 伪代码,发起网络连接,需要占用系统资源
def __del__(self):
self.conn.close() # 关闭网络连接,回收系统资源
obj=MySQL('127.0.0.1',3306) # 在对象obj被删除时,自动触发obj.__del__()
(3)isinstance(obj,cls)
和issubclass(sub,super)
isinstance(obj,cls)
检查是否obj
是否是类cls
的对象
class Foo(object):
2 pass
3
4 obj = Foo()
5
6 isinstance(obj, Foo)
issubclass(sub, super)
检查sub
类是否是super
类的派生类
class Foo(object):
pass
class Bar(Foo):
pass
issubclass(Bar, Foo)
(4)__doc__
class Foo:
'我是描述信息'
pass
print(Foo.__doc__)
class Foo:
'我是描述信息'
pass
class Bar(Foo):
pass
print(Bar.__doc__) #该属性无法继承给子类
(5)__enter__
和__exit__
# 我们知道在操作文件对象的时候可以这么写
with open('a.txt') as f:
'代码块'
- 上述叫做上下文管理协议,即with语句
- 为了让一个对象兼容with语句,必须在这个对象的类中声明
__enter__
和__exit__
方法
- 为了让一个对象兼容with语句,必须在这个对象的类中声明
class Open:
def __init__(self,name):
self.name=name
def __enter__(self):
print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
# return self
def __exit__(self, exc_type, exc_val, exc_tb):
print('with中代码块执行完毕时执行我啊')
with Open('a.txt') as f:
print('=====>执行代码块')
# print(f,f.name)
__exit__()
中的三个参数分别代表- 异常类型
- 异常值
- 追溯信息
- with语句中代码块出现异常,则with后的代码都无法执行
class Open:
def __init__(self,name):
self.name=name
def __enter__(self):
print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
def __exit__(self, exc_type, exc_val, exc_tb):
print('with中代码块执行完毕时执行我啊')
print(exc_type)
print(exc_val)
print(exc_tb)
with Open('a.txt') as f:
print('=====>执行代码块')
raise AttributeError('***着火啦,救火啊***')
print('0'*100) #------------------------------->不会执行
- 如果
__exit__()
返回值为True,那么异常会被清空,就好像啥都没发生一样- with后的语句正常执行
class Open:
def __init__(self,name):
self.name=name
def __enter__(self):
print('出现with语句,对象的__enter__被触发,有返回值则赋值给as声明的变量')
def __exit__(self, exc_type, exc_val, exc_tb):
print('with中代码块执行完毕时执行我啊')
print(exc_type)
print(exc_val)
print(exc_tb)
return True
with Open('a.txt') as f:
print('=====>执行代码块')
raise AttributeError('***着火啦,救火啊***')
print('0'*100) #------------------------------->会执行
(6)__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
(7)__setitem__
,__getitem__
,__delitem__
class Foo:
def __init__(self,name):
self.name=name
def __getitem__(self, item):
print(self.__dict__[item])
def __setitem__(self, key, value):
self.__dict__[key]=value
def __delitem__(self, key):
print('del obj[key]时,我执行')
self.__dict__.pop(key)
def __delattr__(self, item):
print('del obj.key时,我执行')
self.__dict__.pop(item)
f1=Foo('sb')
f1['age']=18
f1['age1']=19
del f1.age1
del f1['age']
f1['name']='alex'
print(f1.__dict__)
(8)__call__
- 对象后面加括号,触发执行。
注:构造方法的执行是由创建对象触发的
即:对象 = 类名() ;
而对于
__call__
方法的执行是由对象后加括号触发的 即:对象() 或者 类()()
class Foo:
def __init__(self):
pass
def __call__(self, *args, **kwargs):
print('__call__')
obj = Foo() # 执行 __init__
obj() # 执行 __call__
本文来自博客园,作者:Chimengmeng,转载请注明原文链接:https://www.cnblogs.com/dream-ze/p/17492101.html