首先了解什么是包装和授权。
我们知道,面向对象程序设计中有一个理念就是封装,而实际上面向对象也让程序员在使用一些类(自己定义或则别人定义)对象的时候非常方便。包装就是一个希望将某个对象(或类),进行重新打包,装换成另外一种更适合当前使用场合的对外接口。相似娱乐界对明星的包装一样,其实本质没有什么改变,只是表面变得更容易受当下人欢迎而已。用到程序编程中,就是将程序包装成,当前环境跟适合的样子(主要是对外接口)。
一个例子,假如一个类中包含了许多的数据属性,同时也包含了许多的基本操作函数,但是我们要求得这个类的对象的某个值(来自于对这个类对象中的各个数据属性和操作函数的多次操作),那么是不是每次都需要做一遍这样的事情呢?理论上是这样的,有人会说,那为什么不在当初定义类的时候就定义一个这样的函数呢?对!但是,当初定义的时候或许想不到这些,而更为重要的是,如果将某个类定义的过分应用于某个情景,那么这个类就并不好在其他地方使用,而如果把它实际到适合在许多情景中使用,那又会是类变得特别的负责,所以基本上,我们认为类应该设计的比较基本为好。
所以,有没有解决这一问题的办法呢?有,那就是封装!
在不同的应用场景将那个各个方法都较为基础的类包装成一个适合在该地方使用的包装类。
那么包装这个设计思想有哪些方法和以实现呢?
我们说既然是面向对象设计,那自然不能脱离类来讲咯(实际上包装的对象可以是任何对象)。我们找方法的范围也减少了。这样包装的概念就是将一个类所能干的事情,通过不同的方式转换成另外一个类,它们的内容本质(这里主要是说数据信息)不变,只是对外操作的接口做了相应的转变,或者添加了,或者减少了,或者转变了,或者限制了等等。
一个类与另外一个类的关系只有两种一种是继承关系(包括祖先),另外一种是包含的关系。也就是derivation和composition两种关系。
通过继承的方式似乎可以,将原来的类成员先全部继承,然后可以对函数进行重写覆盖,也可以添加性函数等等。这是一个好方法,但是,在有些编程语言中如python2.2之前,基本数据类型不属于类,那样就不能被类继承,就不能进行所谓的包装。
所以还可以通过另外一个方法来做到一点,那就是创建一个类,而这个类往往只包含一个数据成员对象,那就是它要封装的类的对象。通俗的讲,就是让这个封装类包含这个被封装类的实例。然后通过在这个封装类中各种方法的设置,实质上就是从这个包含对象的接口传递、转换其相应的功能到这个包装类上。让外界感觉似乎就是在操作该被封装的类一样。可以看出,封装类在转换的时候有很多弹性,所以完全可以做到前面所说的包装效果。
其实,我们可这么想,在type和class还不是一回事的时候,我们没有办法使用继承的方法来对基本数据类型进行封装,所以只能通过这个手段了。但是现在type和class的统一是基本趋势。但仍然不会有违通过composition的方法来进行包装。实际上两种包装手段可以呈现出一种互补的态势,可能大家有一些各自不适合的地方。
而授权是包装的一个特性,授权的过程,就是说所有的更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性。而实现这一功能的关键便是 getattr() 特殊方法。这个方法的作用是:当试图访问一个实例的属性时,本地属性会首先被找到,如果本地没有就会去类属性和祖先类属性里找,如果那里也没有的话,就会调用实例的 _getattr_() 方法。因此这里就给我们提供了一个将属性访问重定向到被包装的类型(伪父类)的机会。其具体的实现方法便是,在 getattr() 中调用内建的 getattr() 函数:
下面是一个例子,这个例子在原有的类的基础上,增加了几个功能,也就是得到类的创建时间(ctime),修改时间(mtime)和访问时间(atime)
from time import time,ctime
class TimedWrapMe(object):
def __init__(self,obj):
self.__data=obj
self.__ctime=self.__mtime=self.__atime=time()
def get(self):
self.__atime=time()
return self.__data
def gettimeval(self,t_type):
if not isinstance(t_type,str) or t_type[0] not in 'cma':
raise TypeError("argument of 'c','m','a' req'd' ")
print('_%s__%stime' % (self.__class__.__name__,t_type[0]))
return getattr(self,'_%s__%stime' % (self.__class__.__name__,t_type[0]))#attention!get private data attribute
def gettimestr(self,t_type):
return ctime(self.gettimeval(t_type))
def setr(self,obj):
self.__data=obj
self.__mtime=self.__atime=time()
def __repr__(self):
self.__atime=time()
return 'self.__data'
def __str__(self):
self.__atime=time()
return str(self.__data)
def __getattr__(self, item):
self.__atime=time()
return getattr(self.__data,item)
a=TimedWrapMe(9)
print(getattr(a,'_TimedWrapMe__ctime'))
print(a.gettimestr('c'))
print(repr(a))
下面还有一个例子,实现以大写的形式往文件写入:
在写这个例子的时候遇到了点问题
class UpperOpen(object):
def __init__(self,filename,mode='r',buf=-1):
self.file=open(filename,mode,buf)
def __str__(self):
return str(self.file)
def __repr__(self):
return repr(self.file)
def write(self,line):
self.file.write(line.upper())
def __getattr__(self, attr):
return getattr(self.file,attr)
f=UpperOpen('in.txt','w')
f.write('fdsa dsa fd afewqr dsaf e\n')
f.write('i am good')
f.close()
f=UpperOpen('in.txt','r')
for line in f:
print(line)
像上面这样写的时候在3.4.3下,会提示如下错误,说f不能迭代
如果显示的调用iter函数的话是可以的:
f=UpperOpen('in.txt','r')
it=f.__iter__()
for line in it:
print(line)
或者重写iter函数也是可以的
class UpperOpen(object):
def __init__(self,filename,mode='r',buf=-1):
self.file=open(filename,mode,buf)
def __str__(self):
return str(self.file)
def __repr__(self):
return repr(self.file)
def __iter__(self):
return self.file.__iter__()
def write(self,line):
self.file.write(line.upper())
def __getattr__(self, attr):
return getattr(self.file,attr)
f=UpperOpen('in.txt','w')
f.write('fdsa dsa fd afewqr dsaf e\n')
f.write('i am good')
f.close()
f=UpperOpen('in.txt','r')
for line in f:
print(line)
但是getattr不应该会搜索到self.file的__iter__属性吗,不明白为什么这样不行,在《python编程思想 第二版》上,就是那么写的也没有问题,难道又是因为我下的是3.4.3?希望哪位python大神指点