面向对象进阶
Posted 你的泪我的眼
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面向对象进阶相关的知识,希望对你有一定的参考价值。
参考: http://www.cnblogs.com/linhaifeng/articles/6204014.html
1. isinstance(obj,cls)和issubclass(sub,super)
sinstance(obj,cls)检查是否obj是否是类 cls 的对象
class Foo(object): pass obj = Foo() isinstance(obj, Foo)
issubclass(sub, super)检查sub类是否是 super 类的派生类
class Foo(object): pass class Bar(Foo): pass issubclass(Bar, Foo)
2. 反射
1 什么是反射
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用,并在Lisp和面向对象方面取得了成绩。
2 python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
四个可以实现自省的函数
下列方法适用于类和对象(一切皆对象,类本身也是一个对象)
判断object中有没有一个name字符串对应的方法或属性
def getattr(object, name, default=None): # known special case of getattr """ getattr(object, name[, default]) -> value Get a named attribute from an object; getattr(x, \'y\') is equivalent to x.y. When a default argument is given, it is returned when the attribute doesn\'t exist; without it, an exception is raised in that case. """ pass getattr(object, name, default=None)
def setattr(x, y, v): # real signature unknown; restored from __doc__ """ Sets the named attribute on the given object to the specified value. setattr(x, \'y\', v) is equivalent to ``x.y = v\'\' """ pass setattr(x, y, v)
def delattr(x, y): # real signature unknown; restored from __doc__ """ Deletes the named attribute from the given object. delattr(x, \'y\') is equivalent to ``del x.y\'\' """ pass delattr(x, y)
class BlackMedium: feature=\'Ugly\' def __init__(self,name,addr): self.name=name self.addr=addr def sell_house(self): print(\'%s 黑中介卖房子啦,傻逼才买呢,但是谁能证明自己不傻逼\' %self.name) def rent_house(self): print(\'%s 黑中介租房子啦,傻逼才租呢\' %self.name) b1=BlackMedium(\'万成置地\',\'回龙观天露园\') #检测是否含有某属性 print(hasattr(b1,\'name\')) print(hasattr(b1,\'sell_house\')) #获取属性 n=getattr(b1,\'name\') print(n) func=getattr(b1,\'rent_house\') func() # getattr(b1,\'aaaaaaaa\') #报错 print(getattr(b1,\'aaaaaaaa\',\'不存在啊\')) #设置属性 setattr(b1,\'sb\',True) setattr(b1,\'show_name\',lambda self:self.name+\'sb\') print(b1.__dict__) print(b1.show_name(b1)) #删除属性 delattr(b1,\'addr\') delattr(b1,\'show_name\') delattr(b1,\'show_name111\')#不存在,则报错 print(b1.__dict__) 四个方法的使用演示
class Foo(object): staticField = "old boy" def __init__(self): self.name = \'wupeiqi\' def func(self): return \'func\' @staticmethod def bar(): return \'bar\' print getattr(Foo, \'staticField\') print getattr(Foo, \'func\') print getattr(Foo, \'bar\') 类也是对象
#!/usr/bin/env python # -*- coding:utf-8 -*- import sys def s1(): print \'s1\' def s2(): print \'s2\' this_module = sys.modules[__name__] hasattr(this_module, \'s1\') getattr(this_module, \'s2\') 反射当前模块成员
导入其他模块,利用反射查找该模块是否存在某个方法
#!/usr/bin/env python # -*- coding:utf-8 -*- def test(): print(\'from the test\') ======================== #!/usr/bin/env python # -*- coding:utf-8 -*- """ 程序目录: module_test.py index.py 当前文件: index.py """ import module_test as obj #obj.test() print(hasattr(obj,\'test\')) getattr(obj,\'test\')()
3. 反射的好处
好处一:实现可插拔机制
有俩程序员,一个lili,一个是egon,lili在写程序的时候需要用到egon所写的类,但是egon去跟女朋友度蜜月去了,还没有完成他写的类,lili想到了反射,使用了反射机制lili可以继续完成自己的代码,等egon度蜜月回来后再继续完成类的定义并且去实现lili想要的功能。
总之反射的好处就是,可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能
class FtpClient: \'ftp客户端,但是还么有实现具体的功能\' def __init__(self,addr): print(\'正在连接服务器[%s]\' %addr) self.addr=addr
#from module import FtpClient f1=FtpClient(\'192.168.1.1\') if hasattr(f1,\'get\'): func_get=getattr(f1,\'get\') func_get() else: print(\'---->不存在此方法\') print(\'处理其他的逻辑\') 不影响lili的代码编写
好处二:动态导入模块(基于反射当前模块成员)
m=__import__("m1.t") #导入的只是最顶层 print(m) import importlib n=importlib.import_module("m1.t") #用这种, 基于字符串导入模块 print(n) 结果: <module \'m1\' from \'/opt/sphinx_note/source/test/m1/__init__.pyc\'> <module \'m1.t\' from \'/opt/sphinx_note/source/test/m1/t.pyc\'>
补充: python一切解对象 ,所以 反射表现的几个函数不但可以用于 从外部导入的模块,也可用于模块自身
import sys obj1=sys.modules[__name__] print(obj1) print(hasattr(obj1,"say_hello")) ### <module \'__main__\' from \'/opt/sphinx_note/source/test/test.py\'> False
3. __setattr__,__delattr__,__getattr__
内置方法,类中没有定义用默认的,定义了用 自定义的
class Foo: x=1 def __init__(self,y): self.y=y def __getattr__(self, item): print(\'----> from getattr:你找的属性不存在\') #默认的在attr不存在会报错 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 三者的用法演示
#自定义举例
class Foo: def __init__(self,name): self.name=name def __getattr__(self, item): # 当属性不存在时候,不会像原来报错,会打印提示信息 print(\'你找的属性【%s】不存在\' %item) def __setattr__(self, k,v): #设置属性前判断是否是str print(\'执行setattr\',k,v) if type(v) is str: print(\'开始设置\') # self.k=v #触发__setattr__ self.__dict__[k]=v.upper() else: print(\'必须是字符串类型\') def __delattr__(self, item): print(\'不允许删除属性【%s】\' %item) # print(\'执行delattr\',item) # del self.item # self.__dict__.pop(item) f1=Foo(\'alex\') # f1.age=18 #触发__setattr__ # print(f1.__dict__) # print(f1.name) # print(f1.age) # print(f1.gender) # print(f1.slary) print(f1.__dict__) del f1.name print(f1.__dict__)
4. 二次加工标准类型(包装)
包装:python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景下我们都需要基于标准数据类型来定制我们自己的数据类型,新增/改写方法,这就用到了我们刚学的继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工,包装依赖于继承。
class List(list): #继承list所有的属性,也可以派生出自己新的,比如append和mid def append(self, p_object): \' 派生自己的append:加上类型检查\' if not isinstance(p_object,int): raise TypeError(\'must be int\') super().append(p_object) @property def mid(self): \'新增自己的属性\' index=len(self)//2 return self[index] l=List([1,2,3,4]) print(l) l.append(5) print(l) # l.append(\'1111111\') #报错,必须为int类型 print(l.mid) #其余的方法都继承list的 l.insert(0,-123) print(l) l.clear() print(l) 二次加工标准类型(基于继承实现)
class List(list): def __init__(self,item,tag=False): super().__init__(item) self.tag=tag def append(self, p_object): if not isinstance(p_object,str): raise TypeError super().append(p_object) def clear(self): if not self.tag: raise PermissionError super().clear() l=List([1,2,3],False) print(l) print(l.tag) l.append(\'saf\') print(l) # l.clear() #异常 l.tag=True l.clear() 练习(clear加权限限制)
授权:授权是包装的一个特性, 包装一个类型通常是对已存在的类型的一些定制,这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。
实现授权的关键点就是覆盖__getattr__方法,不依赖继承。实现的方式是 类的组合.
详细参考: http://www.pythontip.com/blog/post/5225/
__getattr__()与授权:
在Python 2.2 以前,标准类型还不可以派生。为解决此问题而常被使用的手段是“包装”,这种方式的行为就和他的名字一样:
class WrappedList(): def __init__(self,obj): self.__data = obj
但这还不够,我们要实现的主要目标是在需要的时候可以像子类一样自动访问被包装对象的属性或方法。而实现这一功能的关键便是 __getattr__() 特殊方法。这个方法的作用是:当试图访问一个实例的属性时,本地属性会首先被找到,如果本地没有就会去类属性和祖先类属性里找,如果那里也没有的话,就会调用实例的 __getattr__() 方法。因此这里就给我们提供了一个将属性访问重定向到被包装的类型(伪父类)的机会。其具体的实现方法便是,在 __getattr__() 中调用内建的 getattr() 函数:
class WrappedList():
def __init__(self,obj):
print (\'__init__调用\')
self.__data = obj
def __getattr__(self,attr):
print ("__getattr__ 调用伪父类")
return getattr(self.__data,attr)
def __str__(self):
print (\'print 会调用__str__\')
return str(self.__data)
__repr__ = __str__
if __name__ == \'__main__\':
a = WrappedList([1,2,3]) #调用本地__init__()方法.
print (a) #print 会去调用 __str__
a.append(1) #本地没有一个append方法,调用__getattr__(),该方法会调用伪类(内置的list类)的append方法.
print (a)
运行结果:
__init__调用
print 会调用__str__
[1, 2, 3]
__getattr__ 调用伪父类
print 会调用__str__
[1, 2, 3, 1]
不过这里仍有一个缺陷便是,当你对包装对象进行特殊行为时,例如上面 WrappedList 的切片操作,就会遇到问题。因为这已经超出了访问属性的范畴。因此在标准类型已经可以派生的现在,就没必要再去包装他们了,至于其他用途么,也可以先尝试用装饰器去实现。
授权示范一:
python3中open()才有encoding参数 class FileHandle: def __init__(self,filename,mode=\'r\',encoding=\'utf-8\'): self.file=open(filename,mode,encoding=encoding) #类的结合实现授权 def write(self,line): #自定义方法,不使用组合类中方法 t=time.strftime(\'%Y-%m-%d %T\') self.file.write(\'%s %s\' %(t,line)) def __getattr__(self, item): #FileHandle类和它的父类中找不到的属性方法,会触发该函数。而这里我们返回组合类的方法。 return getattr(self.file,item) f1=FileHandle(\'b.txt\',\'w+\') #文件对象 f1.write(\'你好啊\') #更新得功能交给上面的新的write来处理 f1.seek(0) #seek方法上面没有定义,会去触发__getattr__,去执行文件对象已经存在的默认功能来执行。 print(f1.read()) #同上面的seek方法. f1.close()
#_*_coding:utf-8_*_ __author__ = \'Linhaifeng\' #我们来加上b模式支持 import time class FileHandle: def __init__(self,filename,mode=\'r\',encoding=\'utf-8\'): if \'b\' in mode: self.file=open(filename,mode) else: self.file=open(filename,mode,encoding=encoding) self.filename=filename self.mode=mode self.encoding=encoding def write(self,line): if \'b\' in self.mode: if not isinstance(line,bytes): raise TypeError(\'must be bytes\') self.file.write(line) def __getattr__(self, item): return getattr(self.file,item) def __str__(self): if \'b\' in self.mode: res="<_io.BufferedReader name=\'%s\'>" %self.filename else: res="<_io.TextIOWrapper name=\'%s\' mode=\'%s\' encoding=\'%s\'>" %(self.filename,self.mode,self.encoding) return res f1=FileHandle(\'b.txt\',\'wb\') # f1.write(\'你好啊啊啊啊啊\') #自定制的write,不用在进行encode转成二进制去写了,简单,大气 f1.write(\'你好啊\'.encode(\'utf-8\')) print(f1) f1.close() 授权示范二
#练习一 class List: def __init__(self,seq): self.seq=seq def append(self, p_object): \' 派生自己的append加上类型检查,覆盖原有的append\' if not isinstance(p_object,int): raise TypeError(\'must be int\') self.seq.append(p_object) @property def mid(self): \'新增自己的方法\' index=len(self.seq)//2 return self.seq[index] def __getattr__(self, item): return getattr(self.seq,item) def __str__(self): return str(self.seq) l=List([1,2,3]) print(l) l.append(4) print(l) # l.append(\'3333333\') #报错,必须为int类型 print(l.mid) #基于授权,获得insert方法 l.insert(0,-123) print(l) #练习二 class List: def __init__(self,seq,permission=False): self.seq=seq self.permission=permission def clear(self): if not self.permission: raise PermissionError(\'not allow the operation\') self.seq.clear() def __getattr__(self, item): return getattr(self.seq,item) def __str__(self): return str(self.seq) l=List([1,2,3]) # l.clear() #此时没有权限,抛出异常 l.permission=True print(l) l.clear() print(l) #基于授权,获得insert方法 l.insert(0,-123) print(l) 练习题(授权)
5. __getattribute__
参考: http://www.jb51.net/article/87073.htm
class Foo: def __init__(self,x): self.x=x def __getattr__(self, item): print(\'执行的是我\') # return self.__dict__[item] f1=Foo(10) print(f1.x) f1.xxxxxx #不存在的属性访问,触发__getattr__ 回顾__getattr__
class Foo: def __init__(self,x): self.x=x def __getattribute__(self, item): print(\'不管是否存在,我都会执行\') f1=Foo(10) f1.x f1.xxxxxx
#_*_coding:utf-8_*_ __author__ = \'Linhaifeng\' class Foo: def __init__(self,x): self.x=x def __getattr__(self, item): print(\'执行的是我\') # return self.__dict__[item] def __getattribute__(self, item): print(\'不管是否存在,我都会执行\') raise AttributeError(\'哈哈\') f1=Foo(10) f1.x f1.xxxxxx #当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError 二者同时出现