反射、定制数据类型
isinstance,issubclass
isinstance(obj,cls) ,检查obj是否是类cls的对象
class Foo(object): pass obj = Foo() print(isinstance(obj,Foo))
issubclass(sub,super)检查sub类是否是super类的派生类
class Foo: pass class sub(Foo): pass print(issubclass(sub,Foo))
反射:getattr,setattr,delattr,hasattr
hasattr:查看对象中是否有此字符串属性,返回Ture或False
class Foo: def __init__(self,name): self.name = name f = Foo('Mitsui') print(hasattr(f,'name'))
getattr:查看对象是否有此属性,返回属性的值。
class Foo: country = 'China' def __init__(self,name): self.name = name f = Foo('Mitsui') res = getattr(f,'name') res1 = getattr(f,'country') print(res1,res)
setattr:setattr(obj,str,value)设置对象的属性
setattr(f,'age',18) print(f.__dict__) print(f.age)
delattr:delattr(obj,str)删除对象的属性
delattr(f,'name') print(f.__dict__) print(f.name) >>>:
{'age': 18} print(f.name) AttributeError: 'Foo' object has no attribute 'name'
一切皆对象,类也是对象,也可以调用以上的方法。
当前位置导入由当前文件转换的模块:
import sys this_module = sys.modules[__name__]
基于这些反射方法,可以实现代码可插拔机制:
在代码合作编写阶段,一方不需等另一方的编写完成,只需了解他所具备的属性功能便可自行编写自己的代码,
这样可以确保自己不受他人的影响。比如下面这一段代码:
Client端: class FtpClient: 'ftp客户端,但是还没有实现具体的功能' def __init__(self,addr): print('正在连接服务器[%s]'% addr) self.addr = addr Server端: from FtpClient import FtpClient f1 = FtpClient('192.168.1.1') if hasattr(f1,'get'): func_get = getattr(f1,'get') func_get() else: print('----->还未存在此方法') print('先处理其它逻辑') 执行结果: >>>>: 正在连接服务器[192.168.1.1] ----->还未存在此方法 先处理其它逻辑
当client端get方法还未编写,不影响server端代码的运行,而当Client端编写好get方法
此时运行Server端会根据代码做出相应的变动
class FtpClient: 'ftp客户端,但是还没有实现具体的功能' def __init__(self,addr): print('正在连接服务器[%s]'% addr) self.addr = addr def get(self): print('get正在运行') >>>>: 正在连接服务器[192.168.1.1] get正在运行
通过字符串导入模块:
m = input('输入你要导入的模块') m1 = __import__(m)(不建议) == m1 = importtlib.import_module(m)(官方建议方法)
内置attr:__getattr__,__setattr__,__delattr__
__getattr__:当对象执行不存在的属性时,会触发__getattr__
class Foo: def __init__(self,name): self.name = name def __getattr__(self, item): print('__getattr__已触发') f1 = Foo('Mitsui') print(f1.name) print(f1.names) >>>>: Mitsui __getattr__已触发 None #有返回值默认是None
__setattr__:为一个对象的属性赋值时,将触发此方法,可以由此限定参数类型:
class Foo: def __init__(self,name): self.name = name def __setattr__(self, key, value): if not isinstance(value,str): raise TypeError('must be str!') self.__dict__[key] = value f1 = Foo('Mitsui') print(f1.name) f1.name = 'BOBO' print(f1.name) #当f1.name = 123执行时会触发抛出的异常TypeError,f1.name = 'BOBO'则会正常运行。
__delattr__:采用del删除属性时触发,一样可以限制删除功能主动抛出异常。
定制自己的数据类型:
1.继承的方式
继承list的方法:
class List(list):
def append(self, p_object):
if not isinstance(p_object,int):
#派生自己的append:加上类型检查
raise TypeError('must be int')
super().append(p_object)
def insert(self, index, p_object):
if not isinstance(p_object,int):
raise TypeError('must be int')
super().insert(index,p_object)
@property
def mid(self):
'也可以新增自己的属性'
index = len(self) // 2
return self[index]
#如果没有派生跟新增属性,其余的方法都会继承list方法
l = List([1,2,3])
print(l)
l.append(5)
l.insert(1,'234')
print(l)
2.授权的方式
授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。
其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。
#定制一个具有日志功能的文件操作类方法: import time class Open: def __init__(self,filepath,m='r',encode='utf-8'): #传入打开文件所需的参数 self.x=open(filepath,mode=m,encoding=encode) #拿到文件的句柄self.x self.filepath=filepath self.mode=m self.encoding=encode def write(self,line): #定制自己的文件写功能 print('f自己的write',line) t=time.strftime('%Y-%m-%d %X') #得到一个格式输出的时间 年Y月m日d 时分秒X self.x.write('%s %s' %(t,line)) #根据句柄引用文件原本功能‘write’写入文件的内容,包含当前时间以及传入的值 #write功能实现了,文件操作还有其它诸如seek、read等功能,而这些功能开发时可能不需要定制,open又不能继承,该怎么解决呢 #把每个功能重写一遍?那样效率太低太繁琐了,这时候我们便可以用到__getattr__ 当使用者在调用read等方法时,Open类自然找 #不到这个功能,找不到的时候会执行什么呢?没错就是__getattr__ 方法,此时编写一个__gettattr__方法,调用原本属于文件 #操作的功能,问题也就迎刃而解了。 def __getattr__(self, item): # print('=------>',item,type(item)) return getattr(self.x,item) #self.x等于文件句柄 用getattr方法,拿到句柄里面的item方法。即 #getattr(self.x,item)当输入的参数item=read 则相当于拿到了文件初始的read方法,加括号即可运行 f = Open('a.txt','w+') f.write('11111111111111') f.seek(0) print(f.read()) >>>: f自己的write 11111111111111 2017-04-24 17:26:01 11111111111111
思考:
基于授权定制自己的列表类型,要求定制的自己的__init__方法,
定制自己的append:只能向列表加入字符串类型的值
定制显示列表中间那个值的属性(提示:property)
其余方法都使用list默认的(提示:__getattr__加反射)
1 class List: 2 def __init__(self,v): 3 self.x = v 4 def __str__(self): 5 return str(self.x) 6 def append(self,value): 7 if not isinstance(value,str): 8 raise TypeError('must be str') 9 self.x.append(value) 10 @property 11 def mid(self): 12 return self.x[(len(self.x)//2)] 13 14 def __getattr__(self, item): 15 return getattr(self.x,item)