• 反射、元类


    一、反射

    1、什么是反射

    反射是在程序运行的过程中“动态”获取对象的信息,也就是说程序执行到了对象这一步的时候才会去获取对象的信息。

    2、为什么要用反射

    假设两个程序配和使用,第一个程序的将运行得到的数据传给第二个程序继续运行,第二个程序并不知道数据的信息(值、类型),盲目的执行肯定不行,这个时候就可以用到反射对数据进行查询、获取、等相应的操作。

    3、如何实现反射

    在python中实现反射很简单,python提供了一些内置函数来帮我们实现反射。

    class People:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def say(self):
            print(f'{self.name},{self.age}')
    
    
    obj = People('喜欢唱调rap同学', 18)
    # 实现反射机制的步骤
    # 1、先通过多dir:查看出某一个对象下可以.出哪些属性来
    # print(dir(obj))
    
    # 2、可以通过字符串反射到真正的属性上,得到属性值
    # print(obj.__dict__[dir(obj)[-2]])  #喜欢唱调rap同学
    
    

    接下来就是想办法通过字符串来操作对象的属性了,这就涉及到内置函数hasattr、getattr、setattr、delattr的使用了,四个函数可以通过字符串操作对象的属性

    3.1、hasattr() 判断对象中有无相应属性,有返回True,没有返回False

    print(hasattr(obj,'name'))
    #True
    print(hasattr(obj,'x'))
    #False
    

    3.2、setattr() 设置属性的值

    setattr(obj,'name','Egon')
    #相当于obj,name='Egon'
    print(obj.name)
    #Egon
    #如果不存在属性,则不设置,不会报错,注意没有属性不会为对象添加该属性,和自定义类的属性不同
    setattr(obj,'x','Egon')
    
    #用hasattr()做判断,判断之后再设置
    if hasattr(obj,'x'):
        setattr(obj,'x',111111111) # 10.x=11111
    else:
        pass
    
    

    3.3、delattr() 删除属性

    delattr(obj,'name')
    print(obj.__dict__)
    #{'age': 18}
    

    3.4、getattr() 获取属性

    res1=getattr(obj,'say')
    res2=getattr(People,'say')
    
    print(res1)
    #<bound method People.say of <__main__.People object at 0x000001800218A8E0>>
    
    print(res2)
    #<function People.say at 0x0000018003DBA1F0>
    
    #获取没有的属性就会报错
    res3=getattr(obj,'hello')
    #AttributeError: 'People' object has no attribute 'hello'
    
    #指定没有属性时的默认返回值,这样就不会报错
    res3=getattr(obj,'hello','没有')
    print(res3)  #没有
    
    #有属性依旧返回属性的值
    res4=getattr(obj,'say','没有')
    print(res4)
    #<bound method People.say of <__main__.People object at 0x000001E60F6CA8E0>>
    
    #用hasattr()做判断再用getattr()获取属性避免出错
    if hasattr(obj,'x'):
        print(getattr(obj,'x'))
    else:
        pass
    

    使用案例:

    class Ftp:
        def put(self):
            print('正在执行上传功能')
    
        def get(self):
            print('正在执行下载功能')
    
        def interactive(self):
            method=input(">>>: ").strip() # method='put'
    
            if hasattr(self,method):
                getattr(self,method)()
            else:
                print('输入的指令不存在')
    obj=Ftp()
    obj.interactive()
    '''
    >>put
    正在执行上传功能'''
    

    二、类的内置方法

    1、什么是类内置方法:

    Python的Class机制内置了很多特殊的方法来帮助使用者高度定制自己的类,这些内置方法都是以双下划线开头和结尾的,会在满足某种条件时自动触发

    2、为何要用内置方法?

    为了能够更好的定制我们的类或者对象

    3、如何使用内置方法

    以__str__和 __del__为例

    3.1 str 会在打印对象时自动触发,然后将返回值(必须是字符串类型)当做本次打印的结果输出

    class People:
        def __init__(self,name,age):
            self.name=name
            self.age=age
        def __str__(self):
            print('运行了打印功能')
            return f'{self.name}   {self.age}'
    obj = People('喜欢唱调rap同学', 18)
    
    print(obj.__str__())
    # 运行了打印功能
    # 喜欢唱调rap同学18
    
    #等同于直接用print()打印
    print(obj)
    # 运行了打印功能
    # 喜欢唱调rap同学18
    

    3.1 del:在清理对象时触发,会先执行该方法,当程序运行时不仅占用了内存还系统资源值,当程序不需要某些系统资源时,清理对象前告诉操作系统回收相关系统资源(打开的文件)

    class People:
        def __init__(self, name, age):
            self.name = name
            self.age = age
            self.x = open('a.txt',mode='w')
    
            # self.x = 占据的是操作系统资源
    
        def __del__(self):
    		print('run...')
            # 发起系统调用,告诉操作系统回收相关的系统资源
            self.x.close()
    
    
    obj = People('喜欢唱调rap同学',18)
    print('程序运行结束')
    #打印'程序运行结束',程序运行结束,清理对象obj,触发__del__执行,打印'run...''
    '''
    程序运行结束
    run...'''
    
    
    del obj # obj.__del__()
    print('程序运行结束')
    #del obj,触发__del__执行,打印'run...,然后'打印'程序运行结束',程序运行结束。
    
    

    三、元类

    1、引入:

    python中一切皆对象,那么类也是对象,是调用元类实例化而得到的对象。

    2、什么是元类

    元类就是用来实例化产生类的类

    关系:元类---实例化---->类(People)---实例化---->对象(obj)

    class People:
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def say(self):
            print('%s:%s' %(self.name,self.name))
            
    print(People.__dict__)    
    
    #如何得到对象,调用类实例化得到
    # obj=调用类()
    obj=People('egon',18)
    print(type(obj))
    #<class '__main__.People'>
    
    #我们说类也是对象,那么类也是通过调用,实例化而得到的
    # People=调用类(。。。)
    
    1、type是内置的元类
    print(type(People))
    #<class 'type'>
    print(type(int))
    #<class 'type'>
    

    3、class关键字创造类People的步骤

    类有三大特征:

    # 1、类名
    class_name="People"
    # 2、类的基类
    class_bases=(object,)
    # 3、执行类体代码拿到类的名称空间
    class_dic={}
    class_body="""
    def __init__(self,name,age):
        self.name=name
        self.age=age
    
    def say(self):
        print('%s:%s' %(self.name,self.name))
    """
    exec(class_body,{},class_dic)
    print(class_dic)
    

    关于exec的使用:

    #参数一:包含一系列python代码的字符串
    
    #参数二:全局作用域(字典形式),如果不指定,默认为globals()
    
    #参数三:局部作用域(字典形式),如果不指定,默认为locals()
    
    #可以把exec命令的执行当成是一个函数的执行,会将执行期间产生的名字存放于局部名称空间中
    g={
        'x':1,
        'y':2
    }
    l={}
    
    exec('''
    global x,z
    x=100
    z=200
    
    m=300
    ''',g,l)
    
    print(g) #{'x': 100, 'y': 2,'z':200,......}
    print(l) #{'m': 300}
    
    # # 4、调用元类
    People=type(class_name,class_bases,class_dic)
    

    四:如何自定义元类来控制类的产生

    class Mymeta(type): # 只有继承了type类的类才是元类
        #            空对象,"People",(),{...}
        def __init__(self,x,y,z):
            print('run22222222222....')
            print(self)
            print(x)
            # print(y)
            # print(z)
            # print(y)
            # if not x.istitle():
            #     raise NameError('类名的首字母必须大写啊!!!')
    
        #          当前所在的类,调用类时所传入的参数
        def __new__(cls, *args, **kwargs):
            # 造Mymeta的对象
            print('run1111111111.....')
            print(cls,args,kwargs)
            # return super().__new__(cls,*args, **kwargs)
            return type.__new__(cls,*args, **kwargs)
    
    
    People=Mymeta("People",(object,),class_dic)
    

    调用Mymeta发生三件事,调用Mymeta就是type.call

    1、先造一个空对象=>People,调用Mymeta类内的__new__方法

    2、调用Mymeta这个类内的__init__方法,完成初始化对象的操作

    3、返回初始化好的对象

    强调:只要是调用类,那么会一次调用

    1、类内的__new__

    2、类内的__init__

    五:call

    class Foo:
        def __init__(self,x,y):
            self.x=x
            self.y=y
    
        #            obj,1,2,3,a=4,b=5,c=6
        def __call__(self,*args,**kwargs):
            print('===>',args,kwargs)
            return 123
    #
    obj=Foo(111,222)
    print(obj) # obj.__str__
    res=obj(1,2,3,a=4,b=5,c=6) # res=obj.__call__()
    print(res)
    
    应用:如果想让一个对象可以加括号调用,需要在该对象的类中添加一个方法__call__
    总结:
    对象()->类内的__call__
    类()->自定义元类内的__call__
    自定义元类()->内置元类__call__
    

    六:自定义元类控制类的调用=》类的对象的产生

    class Mymeta(type): # 只有继承了type类的类才是元类
        def __call__(self, *args, **kwargs):
            # 1、Mymeta.__call__函数内会先调用People内的__new__
            people_obj=self.__new__(self)
            # 2、Mymeta.__call__函数内会调用People内的__init__
            self.__init__(people_obj,*args, **kwargs)
    
            # print('people对象的属性:',people_obj.__dict__)
            people_obj.__dict__['xxxxx']=11111
            # 3、Mymeta.__call__函数内会返回一个初始化好的对象
            return people_obj
    

    类的调用

    # obj=People('egon',18) =》Mymeta.__call__=》干了3件事
    # 1、Mymeta.__call__函数内会先调用People内的__new__
    # 2、Mymeta.__call__函数内会调用People内的__init__
    # 3、Mymeta.__call__函数内会返回一个初始化好的对象
    

    属性查找原则:对象-》类-》父类

  • 相关阅读:
    DOS批量递归删除文件夹
    根据关键词kill进程
    docker创建镜像的几个命令
    OpenSSL命令---passwd
    A configuration with this name already exists
    查看Linux版本
    ubuntu初次安装后设置root用户密码
    [转载]气象数据集下载网站(包括中国700多个站)
    将界面从屏幕外拖回来方法
    使用GitHub分享代码
  • 原文地址:https://www.cnblogs.com/zhangtieshan/p/12708514.html
Copyright © 2020-2023  润新知