Python之路- 反射&定制自己的数据类型

一.isinstance和issubclass

  • isinstance(obj,cls)检查是否obj是否是类 cls 的对象
  • issubclass(sub, super)检查sub类是否是 super 类的派生类
 1 class Foo:
 2     pass
 3 class Bar(Foo):
 4     pass
 5 f = Foo()
 6 b = Bar()
 7 print(isinstance(f,Foo))
 8 print(issubclass(Bar,Foo))
 9 print(isinstance(Bar,Foo))
10 print(isinstance(b,Foo))
View Code

输出结果如下

1 True
2 True
3 False
4 True
View Code

二.反射和相关的内置函数

  • 反射是指程序可以访问、检测和修改它自己本身状态或者行为的一种能力(自省)。
  • 而在Python中,反射其实就是通过字符串的形式访问或者修改对象的属性。这里的对象是指一切对象,类本身也是一个对象。
 1 class People:
 2     country = 'china'
 3     def __init__(self,name,sex):
 4         self.name = name
 5         self.sex = sex
 6     def walk(self):
 7         print('walk')
 8 p1 = People('silver_bullet','male')
 9 print(hasattr(p1,'name'))              #hasattr(*args, **kwargs) 验证有没有该属性
10 print(getattr(p1,'sex'))               #getattr(object, name, default=None) 获取一个属性,可以设置default
11 print(setattr(p1,'age',22))            #setattr(x, y, v) 设置一个属性,返回值为None,所以用下面get方法验证
12 print(getattr(p1,'age'))
13 print(delattr(p1,'age'))               #delattr(x, 'y') is equivalent to del x.y 删除一个属性,返回值为None
14 # print(getattr(p1,'age'))             #age属性已经被删除掉
15 setattr(p1,'show_name',lambda self:self.name+' is excellent')#增加一个函数属性
16 print(p1.show_name(p1))

 

模块级别的反射

1 import sys
2 xxx = 111
3 this_module = sys.modules[__name__]     #获取当前模块
4 print(hasattr(this_module,'xxx'))
5 print(getattr(this_module,'xxx'))
6 setattr(this_module,'yyy','12345')
7 print(getattr(this_module,'yyy'))

反射的好处

  • 实现可插拔机制。可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,即你可以事先把主要的逻辑写好(只定义接口), 然后后期再去实现接口的功能。 

  • 动态导入模块(基于反射当前模块成员)。
1 import importlib
2 t = importlib.import_module('time')     #通过字符串的形式导入模块,推荐使用importlib
3 print(t.time())                         #不推荐__import__(m),m为字符串,这个是python解释器自己使用的

三.内置函数(__attr__相关)

 1 class Foo:
 2     '内置函数__attr__的使用'
 3     def __init__(self,name):
 4         self.name = name                   #这个也会触发__setattr__的执行
 5     def __setattr__(self, key, value):     #给属性赋值都会触发__setattr__,把key和value都赋值,可以做类型判断,弥补python没有类型限制
 6         self.__dict__[key] = value         #直接在dict字典中添加相应的键值对即可,key和value都为字符串
 7         print('from __setattr__')
 8     def __delattr__(self, item):           #del 操作会触发这个内置函数
 9         self.__dict__.pop(item)            #直接在dict字典中删除相应的键值对即可
10         print('from __delattr__',type(item))# type(item)--------->str
11     def __getattr__(self, item):           #注意:属性获取不到,才会触发这个内置函数!!!
12         print('get__attr__---->{0},{1}'.format(item,type(item)))
13 
14 f = Foo('silver_bullet')                   #触发__init__里就有一个赋值,即会触发__settattr__
15 f.time = 555
16 print(f.__dict__)
17 print(f.time)
18 del f.time                                  #触发__del__内置函数
19 print(f.__dict__)
20 f.xxx                                       #这个才会触发__getattr__的执行,没有找到xxx的属性,而现在却没有报错

四.定制自己的数据类型

  • 继承的方式 

 1 class my_list(list):                   #基于继承
 2     def __init__(self,seq):
 3         for i in seq:
 4             if not isinstance(i,str):
 5                 raise TypeError('This is not a str list')
 6             else:
 7                 super().append(i)
 8     def append(self, object: str):
 9         if not isinstance(object,str):
10             raise TypeError('must be str')
11         super().append(object)
12     @property
13     def mid_value(self):
14         mid_index = len(self)//2
15         return self[mid_index]
16 
17 l = my_list(['a'])
18 print(l)
19 l.append('1')
20 print(l)
21 print(l.mid_value)
22 l.insert(0,4)
23 print(l)
  • 授权的方式  

 1 class my_list():                                 #基于授权
 2     def __init__(self,seq):
 3         for i in seq:
 4             if not isinstance(i,str):
 5                 raise TypeError('This is not a str list')
 6         self.seq = list((seq))                    #使用一个中间属性self.seq保存这个列表
 7     def __str__(self):                            #override  __str__,才能够打印列表的显示,否则打印的是一个对象地址
 8         return str(self.seq)
 9     def append(self, object: str):               #重新定制这个append
10         if not isinstance(object,str):
11             raise TypeError('must be str')
12         self.seq.append(object)                  #实际上是在这里使用
13     @property
14     def mid_value(self):                         #定制一个属性,返回中间值。
15         mid_index = len(self.seq)//2
16         return self.seq[mid_index]
17     def __getattr__(self, item):                 #其余方法都使用list默认的
18         func = getattr(self.seq,item)            #直接返回getattr的函数名
19         return func
20     def __getitem__(self, item):                 #加上以下三个item操作,才能够支持索引,切片,赋值和del删除
21         return self.seq[item]
22     def __setitem__(self, key, value):
23         self.seq[key] = value
24     def __delitem__(self, key):
25         self.seq.pop(key)
26         
27         
28 l = my_list(['a'])
29 print(l)
30 l.append('1')
31 print(l[1])
32 print(l.mid_value)
33 l.insert(0,4)
34 l[0]='123'
35 print(l)
36 print(l[1:])

 

五.给日志文件加时间头

使用授权的方式重写open

 1 import time
 2 class Open:
 3     'override File  write'
 4     def __init__(self,path,mode = 'r',encoding = 'GBK'):
 5         self.fw = open(path,mode=mode,encoding=encoding)        #这里要写成位置参数的形式,取到文件句柄赋值给self.f
 6     def write(self,line):
 7         now_time = time.strftime('%Y-%m-%d-%X')                 #字符串形式显示time时间
 8         self.fw.write('{0}\n'.format(now_time))                 #添加日志文件的时间头
 9         self.fw.write(line)                                     #写想要输入到文件里面的内容
10     def __getattr__(self, item):                                #不想要改写的地方定义在__getattr__中,找不到的属性会触发这个内置函数
11         func = getattr(self.fw,item)                            #通过getattr的方法,将函数名的入口地址获取到并且返回
12         return func
13 
14 f = Open('a.txt','w+')                                           #触发__init__方法
15 for i in range(2):
16     f.write('11111111111\n')
17     time.sleep(0.5)
18 f.seek(0)
19 print(f.readlines())

 

posted @ 2017-04-24 18:41  silver|bullet  阅读(405)  评论(0)    收藏  举报