第三十五篇 类的内置属性(attr属性),包装和授权,__getattr__

双下划线开头的attr方法,都是类内置的方法。

一. 如果没有在类里定义这三个方法,调用的时候就调用类内置的默认的方法

class Too:
    pass

# 类没有定义这三个属性,就用系统默认的方法
t1 = Too()

print(t1.x)  # 只有在属性不存在时, 会自动触发__getattr__
# AttributeError: 'Too' object has no attribute 'x'

del t1.x   # 删除属性时会触发 __delattr__
# AttributeError: 'Too' object has no attribute 'x'

t1.y = 10    # 设置属性的时候会触发 __setattr__

二. 如果你在类里定义了这三个属性,当触发的时候,就会用你自己定义的方法,而不会再去调用Python内置的三个属性了。

# 实例代码
class Foo:
    x = 1
    def __init__(self,y):
        self.y = y
    # __getattr__用处是最大的
    def __getattr__(self, item):
        print("执行__getattr__")

    def __delattr__(self, item):
        print("删除操作__delattr__")
     # del self.item    # 无限递归了
     self.__dict__.pop(item)   
     # 1. 所以删除需要直接使用它,操作底层字典。
# 2. 如果不允许删除,就需要注释掉这句话,就能控制你的所有属性都不被删除
def __setattr__(self, key, value): print("__setattr__开始执行") # self.key = value # 这就无限递归了,不能这么用的哦 # 设置属性最根本的就是在操作最底层的字典属性,所以可以直接改__dict__, 所以应该用下面的方法 self.__dict__[key] = value # 实例化 f1 = Foo(10)

__getattr__:是在调用的属性不存在的时候才会执行

# 调用
print(f1.y)   # 10
print(getattr(f1, 'y'))  # 10

# 调用不存在的属性
f1.ssssss   # 执行__getattr__
'''
__getattr__:是在调用的属性不存在的时候才会执行
'''

__delattr__:执行删除属性操作的时候,会触发__delattr__方法

del f1.y  # 删除操作__delattr__
del f1.x  # 删除操作__delattr__

__setattr__:添加或者修改属性,就会触发__setattr__方法

# 实例化
f1 = Foo(10)   # 实例化的时候,就是在设置字典属性,所以此时会调用一次__setattr__
f1.z = 3       # 再次设置一个z,还会调用__setattr__。
print(f1.__dict__)  # {'y': 10, 'z': 3}
# 因为你重写了__setattr__,凡是赋值操作都会触发它的执行,你啥都没写,就是根本没赋值,除非你直接操作属性字典,否则永远无法赋值。

二次加工标准类型(包装)

__delattr__和 __setattr__作用不大,最有用的是__getattr__,看看它的牛逼之处。

包装:Python为大家提供了标准数据类型,以及丰富的内置方法,其实在很多场景,这些内置方法远远无法满足你的需求,所以我们都需要基于标准数据类型来定制我们自己的数据类型,新增、改写方法,这就用到了继承/派生知识(其他的标准类型均可以通过下面的方式进行二次加工).

包装是通过继承加派生的方式实现的。

class List(list):   # 继承了list类
    # 定制了List自己的append方法,只有字符串类型的才可以append到列表里
    def append(self, p_object):
        if type(p_object) is str:
            # self.append(p_object)  # 无限递归了
            super().append(p_object)   # 等价于 list.append(p_object),前面讲过可以用super().
        else:
            print('只能添加字符串类型')

    # 取中间值,自己定义了一个方法,派生
    def show_midlle(self):
        mid_index=int(len(self)/2)
        return self[mid_index]
l1=List('helloworld')
print(type(l1))   # <class '__main__.List'>
print(l1.show_midlle())  # w
l1.append(1111111111111111111111)    # 只能添加字符串类型

l1.append('SB')
print(l1)   # ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd', 'SB']
调用

授权:授权是包装的一个特性,包装一个类型通常是对已经存在的类型的一些定制,这种做法可以新建,修改,或者删除原有产品的功能。其他的则保持原样。授权的过程,即是所有更新的功能都是由新类的某部分来处理,单已存在的功能就授权给对象的默认属性。

实现授权的关键点就是覆盖__getattr__方法

(1)该示例相当于实现了包装的继承和派生

class FileHandle:
    def __init__(self,filename,mode='r',encoding='utf-8'):
        self.file=open(filename, mode, encoding=encoding)  # 调用的是系统提供open打开文件方法
        self.mode=mode
        self.encoding=encoding

    # def write(self,line):
    #     print('------------>',line)
    #     t=time.strftime('%Y-%m-%d %X')
    #     self.file.write('%s %s' %(t,line))

    def __getattr__(self, item):
        # 这个方式,相当于是文件里有什么方法,我就提供什么
        return getattr(self.file,item)

f1=FileHandle('a.txt','w+')
print(f1.file)  # <_io.TextIOWrapper name='a.txt' mode='w+' encoding='utf-8'>
print(f1.__dict__)
# {'file': <_io.TextIOWrapper name='a.txt' mode='w+' encoding='utf-8'>, 'mode': 'w+', 'encoding': 'utf-8'}
# <_io.TextIOWrapper name='a.txt' mode='w+' encoding='utf-8'>--->这是系统提供的

# 先在f1里找是否有read,然后再类里找,也没有read,最后触发了__getattr__,
# 就把f1传给self, read 传给item,最后用getattr的方式调用self.file,self.file就是f1实例调用自己的file属性
# f1自己的file属性就是上面字典里file,这个file里有,就是系统提供的的open方法
print('==>',f1.read) #触发__getattr__  
#  ==> <built-in method read of _io.TextIOWrapper object at 0x0134BA30>

# 先在自己f1的字典里找没有write方法,然后类FileHandle里也没有write方法,最后触发了__getattr__方法
# 就把f1传给self,把write传给了item,就相当于是get的而是self.write下面的那个write方法,所以就能找到了。
print(f1.write)
# 通过这种方式,f1的write,read, seek,truck等方法都可以操作了

# 牛逼的地方在于,f1是通过FileHandle类实现的,而FildHandle内部是通过__getattr__帮你做了一次中转。
# 通过这种方式,相当于文件的所有属性都传递过来了。
# 这相当于刚才包装的继承和派生。
# 文件操作里有什么,我就给你提供什么
f1.write('1111111111342311\n')
f1.seek(0)
print('--->',f1.read())

(2)上面的示例还没有实现修改的功能

import time
class FileHandle:
    def __init__(self,filename,mode='r',encoding='utf-8'):
        self.file=open(filename, mode, encoding=encoding)  # 调用的是系统提供open打开文件方法
        self.mode=mode
        self.encoding=encoding

    # 写的查找过程是:f1调用write方法,如果找到了就直接执行修改操作,不会再去管__getattr__了
    def write(self,line):  # f1传给self, 写入的内容11111等传给line去接
        # print('------------>',line)
        # 需求是:给写到文件里的内容,都要加上时间
        t=time.strftime('%Y-%m-%d %X')
        self.file.write('%s %s' %(t,line))

    def __getattr__(self, item):
        # 这个方式,相当于是文件里有什么方法,我就提供什么
        return getattr(self.file,item)

f1=FileHandle('a.txt','w+')
f1.write('1111111111342311\n')
f1.write('cpu负载过高\n')
f1.write('内存剩余不足\n')
f1.write('硬盘剩余不足\n')

# 上面写入了,就可以读出来了
f1.seek(0)
print('--->',f1.read())

这个过程,为什么叫授权?哪一步实现了授权?

import time
class FileHandle:
    def __init__(self,filename,mode='r',encoding='utf-8'):
        # 1.self.file=open()类似于组合的方式,赋予了一个文件描述符
        self.file=open(filename, mode, encoding=encoding)
        self.mode=mode
        self.encoding=encoding

    # 3. 如果自己定制了方法,就可以重写原来的方法,实现项目所需要的需求。
    # 比如:写的时候剔除敏感词等
    # 原来的方法什么内容都可以写入,而现在去能够限制你不写入敏感词,这就类似实现了权限管理
    # 这就是授权(不要纠结于叫什么,理解意思)
    def write(self,line):
        t=time.strftime('%Y-%m-%d %X')
        self.file.write('%s %s' %(t,line))

    # 2.利用__getattr__的属性去找想要的属性;
    # 如果不定制,意味着所有的属性、方法都放开了。
    def __getattr__(self, item):
        return getattr(self.file,item)

 

posted @ 2018-07-21 16:39  mamingchen  阅读(285)  评论(0编辑  收藏  举报