原文地址:Python中包装(wrapping)与代理(delegation)
http://blog.sina.com.cn/s/blog_93b45b0f0100zfv7.html
作者:刻卜浪兴

(1)包装(wrapping)的基本思想和内容

(下面主要内容对大部分编程语言适用)

实际上,包装一词是有关程序设计中的,事实上,如果你的系统如果不是较为复杂的话,这个概念基本上不会怎么用到。

我们知道,面向对象程序设计中有一个理念就是封装,而实际上面向对象也让程序员在使用一些类(自己定义或则别人定义)对象的时候非常方便。包装就是一个希望将某个对象(或类),进行重新打包,装换成另外一种更适合当前使用场合的对外接口。相似娱乐界对明星的包装一样,其实本质没有什么改变,只是表面变得更容易受当下人欢迎而已。用到程序编程中,就是将程序包装成,当前环境跟适合的样子(主要是对外接口)。

一个例子,假如一个类中包含了许多的数据属性,同时也包含了许多的基本操作函数,但是我们要求得这个类的对象的某个值(来自于对这个类对象中的各个数据属性和操作函数的多次操作),那么是不是每次都需要做一遍这样的事情呢?理论上是这样的,有人会说,那为什么不在当初定义类的时候就定义一个这样的函数呢?对!但是,当初定义的时候或许想不到这些,而更为重要的是,如果将某个类定义的过分应用于某个情景,那么这个类就并不好在其他地方使用,而如果把它实际到适合在许多情景中使用,那又会是类变得特别的负责,所以基本上,我们认为类应该设计的比较基本为好。

所以,有没有解决这一问题的办法呢?有,那就是封装!

在不同的应用场景将那个各个方法都较为基础的类包装成一个适合在该地方使用的包装类。

那么包装这个设计思想有哪些方法和以实现呢?
我们说既然是面向对象设计,那自然不能脱离类来讲咯(实际上包装的对象可以是任何对象)。我们找方法的范围也减少了。这样包装的概念就是将一个类所能干的事情,通过不同的方式转换成另外一个类,它们的内容本质(这里主要是说数据信息)不变,只是对外操作的接口做了相应的转变,或者添加了,或者减少了,或者转变了,或者限制了等等。

一个类与另外一个类的关系只有两种一种是继承关系(包括祖先),另外一种是包含的关系。也就是derivation和composition两种关系。

通过继承的方式似乎可以,将原来的类成员先全部继承,然后可以对函数进行重写覆盖,也可以添加性函数等等。这是一个好方法,但是,在有些编程语言中如python2.2之前,基本数据类型不属于类,那样就不能被类继承,就不能进行所谓的包装。

所以还可以通过另外一个方法来做到一点,那就是创建一个类,而这个类往往只包含一个数据成员对象,那就是它要封装的类的对象。通俗的讲,就是让这个封装类包含这个被封装类的实例。然后通过在这个封装类中各种方法的设置,实质上就是从这个包含对象的接口传递、转换其相应的功能到这个包装类上。让外界感觉似乎就是在操作该被封装的类一样。可以看出,封装类在转换的时候有很多弹性,所以完全可以做到前面所说的包装效果。

其实,我们可这么想,在type和class还不是一回事的时候,我们没有办法使用继承的方法来对基本数据类型进行封装,所以只能通过这个手段了。但是现在type和class的统一是基本趋势。但仍然不会有违通过composition的方法来进行包装。实际上两种包装手段可以呈现出一种互补的态势,可能大家有一些各自不适合的地方。

(2)Python中通过composition方式包装。

class WrapMe(object):
    def __init__(self, obj): #这个声明了构造函数,实际上就是模拟要包装对象的构建方法
        self.__data = obj #__data就是这个被包装对象的核心,使用__data有一定的隐蔽性
    def get(self):   #放回封装中的数据,这个是添加的方法。
        return self.__data  
    def __repr__(self):  #特殊函数就是Python编译是如何输入这个对象
        return 'self.__data'
    def __str__(self):   #特殊函数,其实就是转换那个__data中的同样的功能
        return str(self.__data)  #特殊函数__str__就是为内建函数准备的
    def __getattr__(self, attr):  #这个是关键,就是代理(delegation) 下面详细讲
        return getattr(self.__data, attr)

我们提到使用composition模式进行需要对被包装得对象的属性(包括数据和方法)进行转换,但是面向对象编程模式是不会容忍需要程序员对该对象所有属性都了如指掌的,这样要求过于苛刻,并且会让整个包装非常麻烦。所以有没有一个很好的方法,将该对象的所有属性(数据和方法)先全部自动转换再说(这种机制要是使用composition就直接解决了)。有!那就是代理。

代理的意思是,“我是一个中间转接的人,你有什么东西传递给我,然后我再转接给另外一个人,我插在中间,隔离了你们的直接交流,但是却也方便了你们,不用那么麻烦,因为我是专门做这一行的。”

其实就是这个语句:
def __getattr__(self, attr):  
        return getattr(self.__data, attr)

感觉很普通是不是,__getattr__是我们之前学过的类对象的特殊函数,它的作用就是定义该对象的“.”(我们平时来引用对象属性的符号)的功能。也就是说定义了这个函数之后,封装好的类实例对象就可以应用更为丰富的东西了。注意这个特殊方法只是在对象找不到相应的方法的时候才会调用的,也就是说如果该对象种存在(__dict__中)那么就不会启动这个函数。(而还有一个特殊函数__getattribute___,就是那种什么时候都会启动的,它才是真正的" . "的代表)

我们可以看到这个函数的内部。它是通过getattr内建函数来获得,被包装对象的特定属性(数据或函数),然后返回给前面,所以意思就是说。通过这样一弄,包装类就可以继承被包装的对象的所有属性了。

>>> wrappedComplex = WrapMe(3.5+4.2j)   #封装了一个复杂数据对象
    >>> wrappedComplex                # wrapped object: repr()
    (3.5+4.2j)
    >>> wrappedComplex.real           # real attribute  
    3.5
    >>> wrappedComplex.imag           # imaginary attribute
    42.2
    >>> wrappedComplex.conjugate()    # conjugate() method
    (3.5-4.2j)
    >>> wrappedComplex.get()          # actual object
    (3.5+4.2j)

我们看到,上面wrappedComlex竟然引用成功了不属于它的属性的属性,这就是特殊函数__getattr__造出来的假象,不过你会发现,在解释器命令行中,系统并不能自动提示那些不是自身属性的属性,这才对!实际上这个" . " 符号就是一个函数引用。

这个封装类可以封装其它的类对象:
    >>> wrappedList = WrapMe([123, 'foo', 45.67])
    >>> wrappedList.append('bar')
    >>> wrappedList.append(123)
    >>> wrappedList
    [123, 'foo', 45.67, 'bar', 123]
    >>> wrappedList.index(45.67)
    2
    >>> wrappedList.count(123)
    2
    >>> wrappedList.pop()
    123
    >>> wrappedList
    [123, 'foo', 45.67, 'bar']

但是:
    >>> wrappedList[3]
   Traceback (innermost last):
     File "<stdin>", line 1, in ?
     File "wrapme.py", line 21, in __getattr__
       return getattr(self.data, attr)
   AttributeError: __getitem__

奇怪了,你不是说只要使用了上面的代理,被包装的对象的属性就全部被代理过来了?这个功能怎么没有被代理过来。

 是的,但是这里很特殊的,wrappedList[3],其实是触发了 __getitem__特殊函数,但是这里并没有定义,所以报错了。当然你完全可以重写__getitem__特殊函数(对应[]),然后映射到被封装的对象上。

(3)更加丰富的包装例子:

这个封装的意义就是将普通的对象封装成,具有创建时间、修改时间、被访问时间属性的对象
1  #!/usr/bin/env python
2
3  from time import time, ctime
4
5  class TimedWrapMe(object):
6
7     def __init__(self, obj):
8        self.__data = obj
9        self.__ctime = self.__mtime = 
10           self.__atime = time()
11
12    def get(self):
13       self.__atime = time()
14       return self.__data
15
16    def gettimeval_r(self, t_type):
17       if not isinstance(t_type, str)  or 
18               t_type[0]  not in 'cma':
19           raise TypeError, 
20           "argument of 'c', 'm', or 'a' req'd"
21       return getattr(self, '_%s__%stime' % 
22           (self.__class__.__name__, t_type[0]))
23
24     def gettimestr(self, t_type):
25        return ctime(self.gettimeval_r(t_type))
26
27     def set(self, obj):
28        self.__data = obj
29        self.__mtime = self.__atime = time()
30
31     def __repr__(self):                # repr()
32        self.__atime = time()
33        return 'self.__data'
34
35     def __str__(self):                 # str()
36        self.__atime = time()
37        return str(self.__data)
38
39     def __getattr__(self, attr):       # delegate
40        self.__atime = time()
41        return getattr(self.__data, attr)

将一个文件对象封装成只能添加大写字符的文件对象(会自动转换成大写字符)

1  #!/usr/bin/env python
2
3  class CapOpen(object):
4      def __init__(self, fn, mode='r', buf=-1):
5          self.file = open(fn, mode, buf)
6
7      def __str__(self):
8          return str(self.file)
9
10     def __repr__(self):
11         return 'self.file'
12
13     def write(self, line):
14         self.file.write(line.upper())
15
16     def __getattr__(self, attr):
17         return getattr(self.file, attr)

[转载]Python中包装(wrapping)与代理(delegation)相关推荐

  1. [转载] Python中的Phyllotaxis模式| 算法植物学的一个单位

    参考链接: Python中的Phyllotaxis模式| 算法植物学的单位 简介| 叶底   Phyllotaxis / phyllotaxy是植物茎上叶子的排列,Phyllotactic螺旋形成自然 ...

  2. [转载] python中for语句用法_详解Python中for循环的使用_python

    参考链接: 在Python中将else条件语句与for循环一起使用 这篇文章主要介绍了Python中for循环的使用,来自于IBM官方网站技术文档,需要的朋友可以参考下 for 循环 本系列前面 &q ...

  3. [转载] python中的for循环对象和循环退出

    参考链接: Python中循环 流程控制-if条件 判断条件,1位true,0是flesh,成立时true,不成立flesh,not取反 if  1; print 'hello python' pri ...

  4. [转载] Python中的memoryview

    参考链接: Python memoryview() Python中的memoryview提供了类似C语言指针的功能,有了memoryview,如果某个Object支持buffer protocol,那 ...

  5. [转载] python中string函数的用法_python中string模块各属性以及函数的用法

    参考链接: Python中的string.octdigits 任何语言都离不开字符,那就会涉及对字符的操作,尤其是脚本语言更是频繁,不管是生产环境还是面试考验都要面对字符串的操作. python的字符 ...

  6. [转载] python中set函数是什么数据类型_Python基本数据类型-list-tuple-dict-set详解

    参考链接: Python中的isdisjoint函数 Python基本数据类型-list-tuple-dict-set 数据类型 表示方法 特性 list 列表用方括号表示:[] list是一种有序的 ...

  7. [转载] Python中关于字符串的使用演示

    参考链接: Python字符串| zfill 注意,python中对于函数的调用基本都是通过.的形式调用的,字符串中除了len()函数,基本都是通过.调用的. 1.字符串变量子串的截取 Python不 ...

  8. [转载] python中numpy.concatenate()函数的使用

    参考链接: Python中的numpy.append numpy库数组拼接np.concatenate 原文:https://blog.csdn.net/zyl1042635242/article/d ...

  9. [转载] python中异常处理的四个句子_Python学习笔记总结(四)异常处理

    参考链接: 在Python中定义清理动作 1.基础 try/except/else:[else是可选的]捕捉由代码中的异常并恢复,匹配except里面的错误,并执行except中定义的代码,后继续执行 ...

最新文章

  1. android功能网格布局,Visual Studio 开发安卓之布局-网格布局(GridLayout)
  2. 03 ORA系列:ORA-00942 表或视图不存在 table or view does not exist
  3. 2个网页跳来跳去_怎么写最优化的网页标题标签(Title Tag)?
  4. Ruby BigDecimal库拒绝服务漏洞
  5. 科目三-变更车道,直线行驶和超车的考试标准
  6. Gartner调查显示政府部门数字化转型尚处萌芽期
  7. 全球链界科技发展大会_科技界女性占五席
  8. css动画逐帧播放、缩放
  9. lintcode 中等题:Divide Two Integers 两个数的除法
  10. 华为百度美团驰援抗击疫情;自由软件基金会建议开源 Windows 7;印度超越美国成第二大智能手机市场 | 极客头条...
  11. 存数据返回他的序列号id_使用excel进行数据分析
  12. linux32-bit是什么意思,怎么查看LINUX系统是32bit还是64bit
  13. aix安装bff_##aix5.3升级到高版本后,安装低BFF文件的问题
  14. System Toolkit for Mac(系统维护工具)
  15. BOOST库介绍(五)——智能指针shared_ptr
  16. 嵌入式--需要懂的网站
  17. 图片文件Exif信息详细说明
  18. 云虚拟主机升级云服务器,云虚拟主机升级
  19. IDEA 自动补充 结尾分号或自动换行
  20. php的表达爱意的一句代码,表达爱意的爱情诗句

热门文章

  1. Spring知识点记录
  2. 华为鸿蒙战略发布会视频,华为公布鸿蒙手机操作系统开机画面视频
  3. java中的Cookie 和 sessions
  4. ODrive实例 #1 电机配置实例(4250-520KV + TLE5012B-E1000)
  5. 前端页面查看PDF文档内容总结
  6. 牛顿下降法和梯度下降法(最速下降法)的速度的比较
  7. 软件质量有什么特性?
  8. mysql语句转为oracle语句
  9. 数据,数据流,数据管道
  10. 穿冰丝很凉快,是有降温作用吗?穿戴冰丝衣物要注意哪些?