反射、定制数据类型

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]
----->还未存在此方法
先处理其它逻辑
View Code
当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正在运行
View Code

通过字符串导入模块:
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
View Code
__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'则会正常运行。
View Code
__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)
View Code
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
View Code

 

思考:

   基于授权定制自己的列表类型,要求定制的自己的__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)
View Code

 

 

 

 
posted @ 2017-04-24 16:18  Mitsuis  阅读(134)  评论(0编辑  收藏  举报