31.授权-可删除或替换

十三、*授权

包装

包装:对一个已存在的对象进行包装,不管它是数据类型,还是一段代码,可以是对一个已存在的对象,增加新的,删除不要的,或者修改其它已存在的功能

包装包括定义一个类,它的实例拥有标准类型的核心行为

 换句话说,它现在不仅能唱能跳,还能够像原类型一样步行,说话.

你还可以包装类,但这不会有太多的用途,因为已经有用于操作对象的机制,并且在上面已描述过,对标准类型有对其进行包装的方式

 

 

 这个图片说明了在类中包装的类型看起来像什么样子,在图的中心为标准类型的核心行为,但他也通过新的或者最新的功能,甚至可能通过访问实际数据的不同方法得到提高

实现授权

授权是包装的一个特性,可用于简化处理有关命令功能,采用已存在的功能以达到最大限度的代码重用

包装一个类型通常是对已存在的类型的一些定制 。这种做法可以新建,修改或删除原有产品的功能。其它的则保持原样,或者保留已存功能和行为。

授权的过程,即是所有更新的功能都是由新类的某部分来处理,但已存在的功能就授权给对象的默认属性
实现授权的关键点就是覆盖__getattr__()方法,在代码中包含一个对 getattr()内建函数的调用

5.特别地,调用 getattr()以得到默认对象属性(数据属性或者方法)并返回它以便访问或调用。

6.特殊方法__getattr__()的工作方式是, 当搜索一个属性时, 任何局部对象首先被找到 (定制的对象)。

如果搜索失败了,则__getattr__()会被调用,然后调用 getattr()得到一个对象的默认行为

7.换言之,当引用一个属性时,Python 解释器将试着在局部名称空间中查找那个名字,比如一个自定义的方法或局部实例属性。如果没有在局部字典中找到,则搜索类名称空间,以防一个类属性被访问。最后,如果两类搜索都失败了,搜索则对原对象开始授权请求,此时,__getattr__()会被调用

包装对象的简例

看一个例子。这个类似乎可以包装任何对象,提供基本功能,比如使用 repr()和 str()来处理字符串表示法。

另外定制由 get()方法处理,它删除包装并且返回原始对象。所以保留的功能都授权给对象的本地属性,在必要时,可由__getattr__()获得。

下面是包装类的例子:

#!/usr/bin/env python
#coding:utf8

class WrapMe(object):
    def __init__(self, obj):
        self.__data = obj
    def get(self):
        return self.__data
    def __repr__(self):
        return 'self.__data'
    def __str__(self):
        return str(self.__data)
    def __getattr__(self, attr):
     "这个方法用于实现授权,即返回的对象可以使用接受到的对象的属性(方法,函数),但是不能使用原有对象的特殊行为" return getattr(self.__data, attr) wrappedComplex = WrapMe(3.5+4.2j) print wrappedComplex # 包装的对象:repr()
#搜索属性的顺序是 解释器将试着在局部名称空间中查找哪个名字,如果没有就搜索类名称空间,最后如果两个搜索都是失败了,
#搜索则对原对象开始授权请求,此时__getattr__()会被调用

print wrappedComplex.real #实部属性
print wrappedComplex.imag # 虚部属性 print wrappedComplex.conjugate() # conjugate()方法 print wrappedComplex.get() # 实际对象

在第一个例子中, 我们将用到复数, 因为所有 Python 数值类型, 只有复数拥有属性: 数据属性,及 conjugate()内建方法(求共轭复数)。

记住,属性可以是数据属性,还可以是函数或方法:

执行结果:

(3.5+4.2j)
3.5
4.2
(3.5-4.2j)
(3.5+4.2j)

一旦我们创建了包装的对象类型, 只要由交互解释器调用 repr(), 就可以得到一个字符串表示。然后我们继续访问了复数的三种属性,我们的类中一种都没有定义。在例子中,寻找实部,虚部及共轭复数的定义。对这些属性的访问,是通过 getattr()方法,授权给对象.

最终调用 get()方法没有授权,因为它是为我们的对象定义的----它返回包装的真实的数据对象。

下一个使用我们的包装类的例子用到一个列表。我们将会创建对象, 然后执行多种操作,每次授权给列表方法。

#!/usr/bin/env python
#coding:utf8

class WrapMe(object):
    def __init__(self, obj):
        self.__data = obj
    def get(self):
        return self.__data
    def __repr__(self):
        return 'self.__data'
    def __str__(self):
        return str(self.__data)
    def __getattr__(self, attr):
        return getattr(self.__data, attr)

wrappedList = WrapMe([123, 'foo', 45.67])
wrappedList.append('bar')
wrappedList.append(123)
print wrappedList
print  wrappedList.index(45.67)
print wrappedList.count(123)
print wrappedList.pop()
print wrappedList

执行结果:

[123, 'foo', 45.67, 'bar', 123]
2
2
123
[123, 'foo', 45.67, 'bar']

注意, 尽管我们正在我们的例子中使用实例, 它们展示的行为与它们包装的数据类型非常相似。然后,需要明白,只有已存在的属性是在此代码中授权的。
特殊行为没有在类型的方法列表中,不能被访问,因为它们不是属性。一个例子是,对列表的切片操作, 它是内建于类型中的, 而不是像 append()方法那样作为属性存在的。 从另一个角度来说,切片操作符是序列类型的一部分,并不是通过__getitem__()特殊方法来实现的。

#!/usr/bin/env python
#coding:utf8

class WrapMe(object):
    def __init__(self, obj):
        self.__data = obj
    def get(self):
        return self.__data
    def __repr__(self):
        return 'self.__data'
    def __str__(self):
        return str(self.__data)
    def __getattr__(self, attr):
        return getattr(self.__data, attr)

wrappedList = WrapMe([123, 'foo', 45.67])
wrappedList.append('bar')
wrappedList.append(123)
print wrappedList
print  wrappedList.index(45.67)
print wrappedList.count(123)
print wrappedList.pop()
print wrappedList
print wrappedList[3] #"getattr()这个函数没有办法做到让对象使用原有对象的特殊行为,比如列表的切片属性"

然而,我们还有一种"作弊"的方法,访问实际对象[通过我们的 get()方法]和它的切片能力

#!/usr/bin/env python
#coding:utf8

class WrapMe(object):
    def __init__(self, obj):
        self.__data = obj
    def get(self):
        return self.__data
    def __repr__(self):
        return 'self.__data'
    def __str__(self):
        return str(self.__data)
    def __getattr__(self, attr):
        return getattr(self.__data, attr)

wrappedList = WrapMe([123, 'foo', 45.67])
wrappedList.append('bar')
wrappedList.append(123)
print wrappedList
print  wrappedList.index(45.67)
print wrappedList.count(123)
print wrappedList.pop()
print wrappedList
realList = wrappedList.get()
print realList[3]



执行结果:

[123, 'foo', 45.67, 'bar', 123]
2
2
123
[123, 'foo', 45.67, 'bar']
bar

你现在可能知道为什么我们实现 get()方法了----仅仅是为了我们需要取得对原对象进行访问这种情况,我们可以从访问调用中直接访问对象的属性,而忽略局部变量(realList):

print wrappedList.get()[3]
'bar'

下面看一下没有__getattr__()方法的类:

#!/usr/bin/env python
#coding:utf8


class WrapMe_compare(object):
    def __init__(self,obj):
        self.__obj=obj;

    def get(self):
        return self.__obj

    def __str__(self):
        return self.__obj

    __repr__=__str__

mc=WrapMe_compare(3.5+4.2j)
print mc.real()  #AttributeError: 'WrapMe_compare' object has no attribute 'real'  这就是没有进行授权

执行结果:

AttributeError: 'WrapMe_compare' object has no attribute 'real'

更新简单的包裹类

1.创建时间,修改时间,及访问时间是文件的几个常见属性,如果你对使用这三类时间顺序数据还不熟,我们将会对它们进行解释。

2.创建时间(或'ctime')是实例化的时间,修改时间(或'mtime')指的是核心数据升级的时间(通常会调用新的 set()方法),

3.而访问时间(或'atime')是最后一次对象的数据值被获取或者属性被访问时的时间戳。

包装标准类型(twrapme.py)

#!/usr/bin/env python
#coding:utf-8


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):
        #gettimeval()方法带一个简单的字符参数,“c”,“m”或“a”,相应地,对应于创建,修改访问时间,并返回相应的时间,以一个浮点值保存
        if not isinstance(t_type, str) or t_type[0] not in 'cma':
            raise TypeError, "argument of 'c', 'm', or 'a' req'd"
            return getattr(self, '_%s__%stime' % (self.__class__.__name__, t_type[0]))

    def gettimestr(self, t_type):
        #gettimestr()仅仅返回一个经 time.ctime()函数格式化的打印良好的字符串形式的时间
        return ctime(self.gettimeval(t_type))

    def set(self, obj):
        self.__data = obj
        self.__mtime = self.__atime = time()

    def __repr__(self): # repr()
        self.__atime = time()
        return 'self.__data'

    def __str__(self): # str()
        self.__atime = time()
        return str(self.__data)

    def __getattr__(self, attr): # delegate
        self.__atime = time()
        return getattr(self.__data, attr)

timeWrappedObj = TimedWrapMe(932)
print timeWrappedObj.gettimestr('c')
print timeWrappedObj.gettimestr('m')
print timeWrappedObj.gettimestr('a')
print timeWrappedObj
print timeWrappedObj.gettimestr('c')
print timeWrappedObj.gettimestr('m')
print timeWrappedObj.gettimestr('a')

执行结果:

Fri Apr 13 22:16:42 2018
Fri Apr 13 22:16:42 2018
Fri Apr 13 22:16:42 2018
932
Fri Apr 13 22:16:42 2018
Fri Apr 13 22:16:42 2018
Fri Apr 13 22:16:42 2018

你将注意到,一个对象在第一次被包装时,创建,修改,及最后一次访问时间都是一样的。一旦对象被访问,访问时间即被更新,但其它的没有动。

如果使用 set()来置换对象,则修改和最后一次访问时间会被更新。例子中,最后是对对象的读访问操作。

#!/usr/bin/env python
#coding:utf-8


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):
        #gettimeval()方法带一个简单的字符参数c,m或a,相应地,对应于创建,修改访问时间,并返回相应的时间,以一个浮点值保存
        if not isinstance(t_type, str) or t_type[0] not in 'cma':
            raise TypeError, "argument of 'c', 'm', or 'a' req'd"
            return getattr(self, '_%s__%stime' % (self.__class__.__name__, t_type[0]))

    def gettimestr(self, t_type):
        #gettimestr()仅仅返回一个经 time.ctime()函数格式化的打印良好的字符串形式的时间
        return ctime(self.gettimeval(t_type))

    def set(self, obj):
        self.__data = obj
        self.__mtime = self.__atime = time()

    def __repr__(self): # repr()
        self.__atime = time()
        return 'self.__data'

    def __str__(self): # str()
        self.__atime = time()
        return str(self.__data)

    def __getattr__(self, attr): # delegate
        self.__atime = time()
        return getattr(self.__data, attr)

timeWrappedObj = TimedWrapMe(932)
print timeWrappedObj.gettimestr('c')
print timeWrappedObj.gettimestr('m')
print timeWrappedObj.gettimestr('a')
print timeWrappedObj
print timeWrappedObj.gettimestr('c')
print timeWrappedObj.gettimestr('m')
print timeWrappedObj.gettimestr('a')
print timeWrappedObj.set('time is up!')
print timeWrappedObj.gettimestr('m')
print timeWrappedObj
print timeWrappedObj.gettimestr('c')
print timeWrappedObj.gettimestr('m')
print timeWrappedObj.gettimestr('a')

执行结果:

Fri Apr 13 22:19:33 2018
Fri Apr 13 22:19:33 2018
Fri Apr 13 22:19:33 2018
932
Fri Apr 13 22:19:33 2018
Fri Apr 13 22:19:33 2018
Fri Apr 13 22:19:33 2018
None
Fri Apr 13 22:19:33 2018
time is up!
Fri Apr 13 22:19:33 2018
Fri Apr 13 22:19:33 2018
Fri Apr 13 22:19:33 2018

改进包装一个特殊对象

创建一个一个包装文件对象的类。我们的类与一般带一个异常的文件对象行为完全一样:在写模式中,字符串只有全部为大写时,才写入文件。
很多老式机器在处理时,严格要求大写字母,所以,我们要实现一个文件对象,其中所有写入文件的文本会自动转化为大写,程序员就不必担心了。
事实上, 唯一值得注意的不同点是并不使用 open()内建函数, 而是调用 CapOpen 类时行初始化。尽管,参数同 open()完全一样。

包装文件对象(capOpen.py)

这个类扩充了 Python FAQs 中的一个例子,提供一个文件类对象,定制 write()方法,同时,给文件对象授权其它的功能

#!/usr/bin/env python
#coding:utf-8


class CapOpen(object):
    def __init__(self, fn, mode='r', buf=-1):
        self.file = open(fn, mode, buf)

    def __str__(self):
        return str(self.file)

    def __repr__(self):
        return 'self.file'

    def write(self, line):
        self.file.write(line.upper())

    def __getattr__(self, attr):
        return getattr(self.file, attr)

f = CapOpen('/tmp/xxx', 'w')
f.write('delegation example
')
f.write('faye is good
')
f.write('at delegating
')
f.close()
print f

执行结果:

<closed file '/tmp/xxx', mode 'w' at 0x7fcf79dc1930>

可以看到,唯一不同的是第一次对 CapOpen()的调用,而不是 open()

原文地址:https://www.cnblogs.com/zhongguiyao/p/11049300.html