• 反射,元类


    一 isinstance与issubstance:

    isinstance判断的是obj是否是cls的对象:

    class Foo:
        def __init__(self,name):
            self.name=name
    
        def tell(self):
            print('self')
    obj=Foo('egon')
    
    print(isinstance(obj,Foo))

    issubstance判断的是子类与父类的关系与否:必须是类与类之间的比较

    issubstance(Foo,object)

    二  反射:

    什么叫反射:python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)

    内置方法:

    下面是反射的一些操作:hasattr,getattr,setattr,delattr:

    class
    Foo: country='China' def __init__(self,name): self.name=name def tell(self): print('tell info') obj=Foo('egon') # print(hasattr(Foo,'country')) # print(hasattr(Foo,'tell')) # print(hasattr(obj,'country')) # print(hasattr(Foo,'tell')) # print(getattr(Foo,'country'))
    print(getattr(obj,'tell',None))      #从obj中取‘tell’属性,如果没有的话会报错,所以我们为了防止报错应该在后面加上‘None’
    setattr(Foo,'age',19) # 为Foo设置age=19
    print(Foo.__dict__) # 类对象调用__dict__方法得到的是其下所有的属性以及对应的值。
    delattr(Foo,'age') # 删除Foo中的属性age
    print(Foo.__dict__)

    下面我们说一种getattr与hasattr连用的方式:

    class Foo:
    #     def __init__(self):
    #       while True:
    #         cmd=input(':').strip()
    #         if hasattr(self,cmd):
    #             func=getattr(self,cmd)
    #             func()
    #     def download(self):
    #         print('download...')
    #     def upload(self):
    #         print('upload....')
    #

    三  内置的方法:__str__  ,__del__  ,__call__,__getattr__是一些满足条件自动触发。其中__getattr__是在对象调一个不存在的属性的时候执行的。

    __str__所表达的意思就是:

    在print打印类的对象的时候会自动触发,__str__下的代码运行。

    class People:
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
        def __str__(self):
            print('=====================================')
            return '''
            name:%s
            age:%s
            sex:%s
            '''%(self.name,self.age,self.sex)
    
    obj=People('feng',18,'male')
    print(People)# 
    print(obj)#相当于打印print(obj.__dict__())

    __del__:

    一般用于回收系统资源,在内存中的对象被删除之前,会触发__del__先回收系统资源。

    例1:
    import time
    class People:
        def __init__(self,name,age,sex):
            self.name=name
            self.age=age
            self.sex=sex
        def __del__(self):#在对象被删除的条件下,自动回收系统资源。
            print('__del__')
    obj=People('eogn',18,'female')
    del obj
    time.sleep(5)
    例2:
    class Mysql:
    # def __init__(self,ip,port):
    # self.ip=ip
    # self.port=port
    # self.conn=connect(ip,port) # 申请系统资源
    #
    # def __del__(self):
    # self.conn.close()
    #
    # obj=Mysql('1.1.1.1',3306)
    
    
    class MyOpen:
    def __init__(self,filepath,mode="r",encoding="utf-8"):
    self.filepath=filepath
    self.mode=mode
    self.encoding=encoding
    self.fobj=open(filepath,mode=mode,encoding=encoding)
    
    def __str__(self):
    msg="""
    filepath:%s
    mode:%s
    encoding:%s
    """ %(self.filepath,self.mode,self.encoding)
    return msg
    
    def __del__(self):
    self.fobj.close()
    
    # f=open('a.txt',mode='r',encoding='utf-8')
    
    f=MyOpen('aaa.py',mode='r',encoding='utf-8')
    # print(f.filepath,f.mode,f.encoding)
    # print(f)
    
    # print(f.fobj)
    res=f.fobj.read()
    print(res)
    del的应用

    __call__:调用对象就会触发__call__,也就是在对象后面加括号()。

    class Foo:
        def __init__(self):
            pass
        def __str__(self):
            return '324'
        def __del__(self):
            pass
        def __call__(self, *args, **kwargs):
            print('__call__',*args,**kwargs)
    
    obj=Foo()
    # print(obj)
    obj(1,2,3,a=2)

    四  元类

    元类:类的类就是元类

    我们一般用class来定义一个类,从而实例化出一系列对象,

    内置的元类type是用来专门产生class定义的类的。那么从这里我们可以看出类就是类型。

    1  补充exec:

    code='''
    x=1
    y=2
    z=3
    '''
    global_dic={}
    local_dic={}
    字符串中的代码没有声明全局,那么就是局部global x---》x=12
    exec (code,{全局名称空间},{局部名称空间})
    exec(code,global_dic,local_dic)
    exec会运行code的代码将产生的全局的名字放到全局名称空间
    print(global_dic)
    print(local_dic)
    
    code='''
    x=1
    y=2
    def f1(self,a,b):
      pass
    '''
    local_dic={}
    exec(code,{},local_dic)
    print(local_dic)##{'x': 1, 'y': 2, 'f1': <function f1 at 0x000000FEFBB12E18>}
    exec是用来运行字符串中的代码,产生的名字放在名称空间中,如果声明了全局名称空间,那么就放到全局里面,
    否则就默认为局部名称。
    exec

    创建类的两种方式:

    第一种:直接用class产生

    class Chinese:
        country='China'
        def __init__(self,name,age):
            self.name=name
            self.age=age
        def talk(self):
            print('%s is talking' %self.name)
    
    print(type(Chinese))#<class 'type'>我们可以看出Chinese就是类,

    第二种:手动模拟class创建类的过程,通过元类type来产生

    type中有三个参数:在实例化的过程type(what,bases=None,dict=None)分别代表了:类名,类的基类,类的名称空间。也就是类的三要素。

    类的组成:类名,类的基类,类的名称空间,其实就是在定义阶段产生的名字放在类所在的名称空间。
    类名
    class_name='Chinese'
    类的基类
    class_bases=(object,)#这里必须要加一个逗号’,‘
    类体
    class_body="""
    country='China'
    def __init__(self,name,age):
        self.name=name
        self.age=age
    def talk(self):
        print('%s is talking' %self.name)
    """
    class_dic={}
    exec(class_body,globals(),class_dic)步骤一(先处理类体->名称空间):类体定义的名字都会存放于类的名称空间中(一个局部的名称空间),
    我们可以事先定义一个空字典,然后用exec去执行类体的代码(exec产生名称空间的过程与真正的class过程类似,只是后者会将__开头的属性变形),
    生成类的局部名称空间,即填充字典
    
    print(class_dic)输出结果为{'country': 'China', 'talk': <function talk at 0x101a560c8>, '__init__': <function __init__ at 0x101a56668>}
    Foo=type(class_name,class_bases,class_dic) #实例化type得到对象Foo,即我们用class定义的类Foo,通过type得到的就是一个类,
    跟我们用class来定义是一样的。
    print(Foo)
    print(type(Foo))
    print(isinstance(Foo,type))
    # 得到的结果为:
    '''
    <class '__main__.Chinese'>
    <class 'type'>
    True
    '''

    我们还可以自定义一个元类type:

    #4 、自定义元类:
    # class Mymeta(type):
    #     # 来控制类Foo的创建
    #     def __init__(self,class_name,class_bases,class_dic): #self=Foo
    #         # print(class_name)
    #         # print(class_bases)
    #         # print(class_dic)
    #         if not class_name.istitle():
    #             raise TypeError('类名的首字母必须大写傻叉')
    #
    #         if not class_dic.get('__doc__'):
    #             raise TypeError('类中必须写好文档注释,大傻叉')
    #
    #         super(Mymeta,self).__init__(class_name,class_bases,class_dic)
    #
    #     # 控制类Foo的调用过程,即控制实例化Foo的过程
    #     def __call__(self, *args, **kwargs): #self=Foo,args=(1111,) kwargs={}
    #         # print(self)
    #         # print(args)
    #         # print(kwargs)
    #
    #         #1 造一个空对象obj
    #         obj=object.__new__(self)
    #
    #         #2、调用Foo.__init__,将obj连同调用Foo括号内的参数一同传给__init__
    #         self.__init__(obj,*args,**kwargs)
    #
    #         return obj
    #
    #
    #
    # #Foo=Mymeta('Foo',(object,),class_dic)
    # class Foo(object,metaclass=Mymeta):
    #     """
    #     文档注释
    #     """
    #     x=1
    #     def __init__(self,y):
    #         self.Y=y
    #
    #     def f1(self):
    #         print('from f1')
    #
    #
    # obj=Foo(1111) #Foo.__call__()
    #
    # # print(obj)
    # # print(obj.y)
    # # print(obj.f1)
    # # print(obj.x)
    自定义元类

    单例模式:也就是说当很多个实例化产生对象的结果是一样的情况下,我们可以使用单例模式,通过绑定@classmethod,让相同的对象都指向同一个地址,节省内存空间。

    import settings
    
    class MySQL:
        __instance=None
        def __init__(self,ip,port):
            self.ip=ip
            self.port=port
    
        @classmethod
        def singleton(cls):
            if not cls.__instance:
                obj=cls(settings.IP, settings.PORT)
                cls.__instance=obj
            return cls.__instance
    
    obj1=MySQL('1.1.1.2',3306)
    obj2=MySQL('1.1.1.3',3307)
    obj3=MySQL('1.1.1.4',3308)
    
    # obj4=MySQL(settings.IP,settings.PORT)
    # print(obj4.ip,obj4.port)
    
    obj4=MySQL.singleton()
    obj5=MySQL.singleton()
    obj6=MySQL.singleton()
    
    print(obj4 is obj5 is obj6)

    单例模式三种方法:

    import setting
    
    class Mysql:
        __instance=None
        def __init__(self,host,port):
            self.host=host
            self.port=port
    
        @classmethod
        def singleton(cls):
            if not cls.__instance:
                cls.__instance=cls(setting.HOST,setting.PORT)
            return cls.__instance
    
    
    obj1=Mysql('1.1.1.2',3306)
    obj2=Mysql('1.1.1.3',3307)
    print(obj1 is obj2) #False
    
    obj3=Mysql.singleton()
    obj4=Mysql.singleton()
    print(obj3 is obj4) #True
    
    
    
    #方式二:定制元类实现单例模式
    import setting
    
    class Mymeta(type):
        def __init__(self,name,bases,dic): #定义类Mysql时就触发
    
            # 事先先从配置文件中取配置来造一个Mysql的实例出来
            self.__instance = object.__new__(self)  # 产生对象
            self.__init__(self.__instance, setting.HOST, setting.PORT)  # 初始化对象
            # 上述两步可以合成下面一步
            # self.__instance=super().__call__(*args,**kwargs)
    
    
            super().__init__(name,bases,dic)
    
        def __call__(self, *args, **kwargs): #Mysql(...)时触发
            if args or kwargs: # args或kwargs内有值
                obj=object.__new__(self)
                self.__init__(obj,*args,**kwargs)
                return obj
    
            return self.__instance
    
    
    
    
    class Mysql(metaclass=Mymeta):
        def __init__(self,host,port):
            self.host=host
            self.port=port
    
    
    
    obj1=Mysql() # 没有传值则默认从配置文件中读配置来实例化,所有的实例应该指向一个内存地址
    obj2=Mysql()
    obj3=Mysql()
    
    print(obj1 is obj2 is obj3)
    
    obj4=Mysql('1.1.1.4',3307)
    
    
    
    # 方式三:定义一个装饰器实现单例模式
    import setting
    
    def singleton(cls): #cls=Mysql
        _instance=cls(setting.HOST,setting.PORT)
    
        def wrapper(*args,**kwargs):
            if args or kwargs:
                obj=cls(*args,**kwargs)
                return obj
            return _instance
    
        return wrapper
    
    
    @singleton # Mysql=Singleton(Mysql)
    class Mysql:
        def __init__(self,host,port):
            self.host=host
            self.port=port
    
    
    
    obj1=Mysql()
    obj2=Mysql()
    obj3=Mysql()
    print(obj1 is obj2 is obj3) #True
    
    obj4=Mysql('1.1.1.3',3307)
    obj5=Mysql('1.1.1.4',3308)
    print(obj3 is obj4) #Fals
    View Code
  • 相关阅读:
    duilib中各控件响应的消息类型
    CoInitialize()、CoInitializeEx()和AfxOleInit()区别联系
    Linux下反斜杠号""引发的思考
    人生规划和GTD——“知”、“得”与“合”
    一次失败的面试——IBM电话面试
    Duilib介绍以及各个类的简介
    Linux与Windows中动态链接库的分析与对比
    VC++创建、调用dll的方法步骤
    网络对抗 Exp0 Kali安装 Week1
    2018-2019-1 20165226_20165310_20165315 实验五 通讯协议设计
  • 原文地址:https://www.cnblogs.com/zhangsanfeng/p/8858356.html
Copyright © 2020-2023  润新知