• 14.继承与授权


    反射

    自省:程序能够访问,检测和修改他本身的状态或者行为的能力

    通过字符串的形式操作对象相关属性。

    class people:
        country = 'China'
        def __init__(self,name):
            self.name = name
    
    p = people('scott')
    people.country
    print(people.__dict__)

    存在:hasatter(p,'name')

    p这个对象下,有没有‘name’这个属性
    其实就是
    print('name' in p.__dict__)
    返回一个bool值

    获得:getattr

    p这个对象下,调用country参数
    gatattr(p,'country')
    等同于
    p.country

    def getattr(object, name, default=None): # known special case of getattr
        """
        getattr(object, name[, default]) -> value
    
        Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y.
        When a default argument is given, it is returned when the attribute doesn't
        exist; without it, an exception is raised in that case.
        """
        pass

    设置:setattr

    setattr(p,'country','China')
    等同于
    p.country = 'China'

    提问:

    >>> l = [1,2,4,5,8]
    >>> setattr(l,'append','China')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'list' object attribute 'append' is read-only

    小结练习:

    class People:#定义一个类
        def __init__(self,name):
            self.name = name#提供一个绑定参数
    
    p1 =People('scott')#实例化p1
    p2 =People('jerry')#实例化p2
    #给p1对象添加一个‘country’属性,值为‘China’
    setattr(p1,'country','China')
    #使用getattr获取这个属性并且打印
    print(getattr(p1,'country'))
    #以上那个country参数,是我添加给对象p1的,查看p2是否也获得了这个参数?-----并没有,这个参数单独被加在p1这个对象中
    print(hasattr(p2,'country'))
    # 使用delattr删除刚才添加的country参数
    delattr(p1,'country')
    #检查
    hasattr(p1,'country')
    #定义一个函数func
    def func(self):
        print(self.name,' like python')
    #利用lambda给p1对象传入一个work方法
    setattr(p1,'work',lambda self: (self.name,'like python') )
    #给p1传入func方法
    setattr(p1,'work2',func )
    #目前两个work方法均是针对p1传值的,但是在下方实例化p1的work2方法为h,并且对两个对象进行work2方法,发现两个对象均可以使用
    #设置setattr可以将一个函数或者参数指向添加给某个对象的类
    #虽然在设置中,设置的P1但是其实和p1同类的实例均可以调用
    
    print(hasattr(p1,'work'))
    
    g = getattr(p1,'work')
    h = getattr(p1,'work2')
    print(g(p1))
    h(p1)
    h(p2)

    反射的用途

    反射当前模块的属性

    import sys
    #导入系统模块
    x = 1111
    class foo:
        pass
    def s1():
        print('s1')
    def s2():
        print('s2')
    this_module = sys.modyles[__name__]
    #将当前写的程序作为一个模块“this_module”
    def add():
        print('add')
    
    def change():
        print('change')
    
    def search():
        print('search')
    
    def delete():
        print('delete')
    
    func_dic = {
        'add':add
        'change':change
        'search':search
        'delete':delete
    }
    • 方法一(常规)
    while True:
        cmd = input('>>:')
        if not cmd:continu
        if cmd in func_dic:        #hasattr()
            func= func_dic.get(cmd)    #gatattr()
            func()
    • 方法二(利用反射实现)
    this_module = sys.modyles[__name__]
    #将以上程序归为一个模块,命名为this_module
    while True:
        cmd = input('>>>').strip()
        if not cmd:continu
        if hasatter(this_module,cmd):
            func = getattr(this_module,cmd)
            func()

    反射实现可插拔机制

    • server
    from ftp_1 import FtpClient
    #从FIP_1中载入FtpClient模块
    f1 = FtpClient('192.168.1.1')
    #实例化
    if hasattr(f1,'get'):
    #查看能不能从对象中获取一个‘get’函数
        func_get = getattr(f1,'get')
        func_get()#如果能获取就运行
    else:
    #如果没有就打出标志位
        print('---------->')

    在上述程序中,如果在FtpClient文件中没有定义一个get函数。并不会影响上述程序执行。但是如果FtpClient文件补全了了这个get函数,上述文件并不需要修改,就能正常实现功能

    • ftp module
    #提供一个FtpClient类
    class FtpClient:
        def __init__(self,addr):
            print('lianjie [%s]'%addr)
            self.addr = addr

    通过字符串导入模块

    m = input('module:')
    m1 = __import__(m)
    print(m1)
    print(m1.time())

    推荐使用以下方法

    import importlib
    t = importlib.import_module('time')
    print(t.time())

    __setattr/delattr/getattr__

    下面这段代码,串讲一些之前提到的setattr、delattr、getattr,并且介绍__setattr/delattr/getattr__

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

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

    class Foo:
        def __init__(self,name):
            self.name = name
        def __setattr__(self,key,value):
            print('----setattr----'key,value)
        #由于传入的key和value都是字符串,不能直接跟在self后面
        #self.key = value
        #setattr(self,key,value)#同上
        #在当前函数中设置这个赋值(因为重写了__setattr__,所有的赋值操作都会指向这个函数运行),就相当于把这个函数变成一个无限递归。
        #正确的方法应该是,直接操作__dict__
        self.__dict__[key] = value
    
        def __delattr__(self,item):
            print('delattr:%s'%item)
            print(type(item))
            # delattr(self,item)
            # del self.item
            self.__dict__.pop(item)
            #原理同上,为了防止递归和字符串类型干扰,应该直接操作__dict__
    
        def __getattr__(self,item);
            print('get------>%s  %s'%(item,type(item)))
    
    f = Foo('scott')
    print(f.name)
    #当一个属性存在的时候,正常寻找self.name
    print(f.xxxxx)
    #当属性不存在的时候,才触发__geattr__
    
    
    f1 = Foo('egon')
    f1.age = 18
    
    print(f1.__dict__)

    字典为空,之前的两次传入调用参数,都直接进入了__setattr__

    定制自己的数据类型

    写一个类,继承原来的list类

    class List(list):
        def append(self,p_object):
            if not isinstance(p_object,int):
                raise TypeError('str')
            super().append(p_object)

    写一个自己的open

    由于open是一个函数,所以不能直接继承

    import time
    
    class Open:
        def __init__(self,filepath,m = 'r',encode = 'utf8'):
            self.x= open(filepath,mode = m,encoding = 'utf8')
            #以上self.x就是一个文件句柄,之后需要原文件操作类赋予的动作均使用这个句柄来进行。
            self.mode = m
            self.encoding = encode
    
        def write(self,line):
            print('time',line)
            self.x.write(time.strftime('%Y-%m-%d %X'),line)
    
        #授权得分
        def __getattr__(self,item):
            print('--->',item,type(item))
            getattr(self.x,item)
        #在当前类找不到某个方法的时候,因为self.x是一个可以调用各种文件操作的句柄,所以使用getattr方法,来获得这些方法,并且在上方提示。
        #有了这个函数定义之后,就可以在f中使用各个文件操作方式。
    
    f =Open('b.txt','w+')
    print(f)
    f.write('111111111111')
    print(f.read())
    f.seek(0)

    二次加工标准类型list

    要求使用授权的方式:

    '''
    作业:
        基于授权定制自己的列表类型,要求定制的自己的__init__方法,
        定制自己的append:只能向列表加入字符串类型的值
        定制显示列表中间那个值的属性(提示:property)
        其余方法都使用list默认的(提示:__getattr__加反射)
    '''
    class List:
        def __init__(self,LOBJECT):
            #将self.x定义为list类包装过的传入参数LOBJECT,以此来获得list的一些方法
            self.x = list(LOBJECT)
    
        #重新定义append方法,使所有添加进来的参数均转换成str数据类型
        def append(self,value):
            print('后添加的元素都是str格式!')
            if not isinstance(value,str):
                self.x.append(str(value))
                # raise TypeError('ERROR')
            else:
                self.x.append(value)
    
        #特征化一个函数,这个函数会返回一个列表的中间值,如果这个列表的元素数量为偶数,就返回中间两个值。
        @property
        def mid_list(self):
            len_th =len(self.x)
            if len_th%2==0:
                f1 = self.x[(len_th//2)]
                f2 = self.x[(len_th//2)-1]
                return f1,f2
            else:
                return self.x[len_th // 2]
        #授权self.x获得其他没有定义的关于list的函数,因为传入的对象为self.x,它属于list类。
        def __getattr__(self, item):
            getattr(self.x,item)
        #为了让List对象在打印的时候也和list对象一样,一旦打印就能获得将传入数据类型变为list的能力,在这里使用__str__,使得每次传入的参数返回出来都是list
        def __str__(self):
            return str(self.x)
    
    y = (4,5,7,8,6)
    
    k = List((1,2,4,5,7))
    l = List([1,2,3,4,5,6])
    # print(l)
    print(k)
    print(List(y))
    # l.append(12)
    print(k.mid_list)

    补充一点__getattribute__

    #_*_coding:utf-8_*_
    __author__ = 'Linhaifeng'
    
    class Foo:
        def __init__(self,x):
            self.x=x
    
        def __getattr__(self, item):
            print('执行的是我')
            # return self.__dict__[item]
        def __getattribute__(self, item):
            print('不管是否存在,我都会执行')
            raise AttributeError('哈哈')
    
    f1=Foo(10)
    f1.x
    f1.xxxxxx
    
    #当__getattribute__与__getattr__同时存在,只会执行__getattrbute__,除非__getattribute__在执行过程中抛出异常AttributeError

    <wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">

  • 相关阅读:
    Windows下安装redis
    flask-redirect
    MySQLHelper
    配置信息
    注释习惯
    SQL2012 附加数据库提示5120错误解决方法
    Union-SQL Server学习笔记
    存储过程
    模糊查询&&日期时间操作
    数据库表的设计
  • 原文地址:https://www.cnblogs.com/scott-lv/p/7468956.html
Copyright © 2020-2023  润新知