• 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()

  • 相关阅读:
    [HAOI2008]糖果传递
    [HAOI2008]木棍分割
    [HAOI2008]硬币购物
    [ZJOI2008]泡泡堂
    [JSOI2007]建筑抢修
    [JSOI2007]麻将
    [Note]prufer
    [BZOJ3275]Number
    [POI2014]RAJ-Rally
    [JSOI2010]快递服务
  • 原文地址:https://www.cnblogs.com/zhongguiyao/p/11049300.html
Copyright © 2020-2023  润新知