zoukankan      html  css  js  c++  java
  • 面向对象之元类,单例

    一.eval与exect内置函数

           eval内置函数的使用场景:

      1.执行字符串会得到相应的执行结果
      2.一般用于类型转化,得到dict、list、tuple等
    
    
    # "{'a': 1, 'b': 2, 'c': 3}" => {'a': 1, 'b': 2, 'c': 3}
    
    data_str="{'a': 1, 'b': 2, 'c': 3}"
    res = eval(data_str)
    print(res, type(res))
    #{'a': 1, 'b': 2, 'c': 3} <class 'dict'>

          exec应用场景:

    #   1.执行字符串没有执行结果(没有返回值)
    #   2.将执行的字符串中产生的名字形成对应的局部名称空间
    
    s = '''
    my_a = 10
    my_b = 20
    def __init__(self):
        pass
    @classmethod
    def print_msg(msg):
        print(msg)
    '''
    l_dic = {}
    exec(s, {}, l_dic) 
    #s是自定义字符串,包含属性和方法,{}是全局名称空间,设为空字典就行,
    #执行exec后s中的内容会给到局部名称空间l_dic字典中
    print(l_dic)
    #{'my_a': 10, 'my_b': 20, '__init__': <function __init__ at 0x01EDEA50>...}

        那么可以用exec创建一个对象的名称空间:

    source = '''
    name = 'Bob'
    age = 20
    '''
    #source是自定义字符串
    

    class A: pass a = A() #这个类中没有init函数,实例化的对象没有创建名称空间
    dic = {} exec(source, {}, dic) #执行exec后source中的内容会给到局部名称空间dic字典中 a.__dict__ = dic # 对象a的名称空间本来为空,现在将dic替换掉a的名称空间 print(a.__dict__) # {'name': 'Bob', 'age': 20} print(a.name) # Bob print(a.age) # 20

    二.元类的基本定义:

     Python中万物皆对象,所有用来创建对象的类,本身也是对象,类是type类的对象
     type类叫做元类,是所有元类(包括自定义元类)的基类
     元类:造类的类 - 也就是类的类
           -- 控制类的产生
           -- 控制类的对象的产生

    三.自定义元类:

        那么我们先来看一下type中有哪几个参数:

    class MyMeClass(type):  
    #这是一个自定义元类,控制User类的产生,这里主要看参数,下面会详细介绍
    def __new__(cls, *args,**kwargs): print(args) # ('User', (<class 'object'>,), {'__module__': '__main__', '__qualname__': 'User', '__init__': <function User.__init__ at 0x07F8D930>}) #第一个是类名,第二是基类,第三个是名称空间
    print(kwargs) # {} return type.__new__(cls,*args,**kwargs) class User(object,metaclass=MyMeClass): def __init__(self,name ): self.name = name

        用type造个类看看,利用 type(classname, basename, namespace)直接造类:

    s = '''
    wudaorong="小可爱"
    
    '''
    namespace ={}
    exec(s,{},namespace)
    Student = type('Student',(object,),namespace)
    #基类可能会继承多个,所以用元组括起来,可以什么都不写,则默认是object
    print(Student.__dict__)
    stu=Student()
    print(stu.wudaorong)

      自定义元类都是继承type的,先来看它的__init__方法:

    class MyMeta(type):
    
        # 自定义元类,重写init方法的目的:
        # 1.该方法是从type中继承来的,所以参数同type的init
        # 2.最终的工作,如果开辟空间,还是要借助type
        # 3.在交给type最终完成工作之前,可以对类的创建加以限制
        def __init__(cls, class_name, bases, namespace):
            # 继承type的init方法
            super().__init__(class_name, bases, namespace)
            #可以查看每个控制的类的各参数
            print(cls)
            print(class_name)
            print(bases)
            print(namespace)
            #自定义除type外新加要求:
            # 需求1:由给元类控制的类的类名必须首字母大写
            if not class_name.istitle():
                raise NameError('名字首字母必须大写')
            # 需求2:定义类是必须明确父类
            if len(bases) == 0:
                raise Exception('父类必须明确')
    
    #创建类方式一:
    s='''
     
    def __init__(self, name, age):
    
            self.name = name
            self.age = age
    '''
    namespace={}
    exec(s,{},namespace)
    Student = MyMeta('Student', (object,), namespace )
    
    #创建类方式二:
    class Student(object, metaclass=MyMeta):
        def __init__(self, name, age):
    
            self.name = name
            self.age = age
    
    #继承仍生效,同时让它也受MyMeta控制
    class Xuesheng(Student, metaclass=MyMeta):
         pass

      除了控制类,我们还可以对它实例化出来的对象进行控制(__call__方法):

    class MyMeta(type):
    
        # 自定义元类,重写call方法的目的:
        # 1.被该元类控制的类生成对象,会调用元类的call方法
        # 2.在call中的返回值就是创建的对象
        # 3.在call中
        #       -- 通过object开辟空间产生对象
        #       -- 用被控制的类回调到自己的init方法完成名称空间的赋值
        #       -- 将修饰好的对象反馈给外界
        def __call__(cls, *args, **kwargs):
            print('我是小胖啊')
            obj = object.__new__(cls)
            # 需求:所有通过该元类控制的类产生的对象都有meta_name该属性
            obj.meta_name = cls.__name__
            # obj.name = args[0]
            # 调回当前被控制的类自身的init方法,完成名称空间的赋值
            cls.__init__(obj, *args, **kwargs)
            return obj
    
    class Student(object, metaclass=MyMeta):
        def __init__(self, name, age):
    
            self.name = name
            self.age = age
    
    #继承仍生效,同时让它也受MyMeta控制
    class Xuesheng(Student, metaclass=MyMeta):
        hobby='打游戏'
    xs=Xuesheng('wdr',18)
    cls_name=xs.meta_name
    name=xs.name
    hobby=xs.hobby
    print(cls_name,name,hobby)
    print(xs.__dict__,Xuesheng.__dict__)
    
    注: 实例化在走init方法先会被自定义元类的__call__方法修饰后才完成:
    class Foo:
    
        def __init__(self):
            print('__init__')
    
        def __call__(self, *args, **kwargs):
            print('__call__')
    
    
    obj = Foo()  # 执行 __init__
    obj()        # 执行 __call__
    可能对__new__方法有疑惑,再来看看__new__:
    1. 类一旦重写__new__方法,该类的实例化由__new__来控制,__init__会失效
    class A:
        def __init__(self,name):
           self.name=name
        def __new__(cls, name):
            cls.name = name
    #下面的的a其实是A元类实例化出来的,即a就是A这个类本身
    #把__new__写在某个类里面就是要自定义这个类
    a= A('小胖')
    print(A.__dict__)
    打印a.__dict__会报错,因为没有走__init__


    2.如果还想控制这个类的对象,要借助object.__new__:
    class A:

    def __init__(self,name):
    self.name=name
    def __new__(cls, name,*args, **kwargs):
    cls.name = name

    obj = object.__new__(cls)
    # 先利用object.__new__开辟了一个对象内存空间,然后赋予属性实例化
    #这里可以可以直接给属性赋值,没有则走__init__,必须把这个对象返回
    return obj
    a= A('小胖')
    print(A.__dict__,a.__dict__)






    四.单例

     单例:一个类只能产生一个实例
     为什么要有单例:
     1.该类需要对象的产生
     2.对象一旦产生,在任何位置再实例化对象,只能得到第一次实例化出来的对象
    3.在对象唯一创建后,可以通过属性修改或利用方法间接修改属性,来完成数据的更新,但不能通过实例化方式更新数据

       方式1 :任何文件在任何地方通过导入拿到的是唯一对象

    class Songs():
        pass
    
    # s1 = Songs()
    # s2 = Songs()
    # print(s1, s2), 产生不同对象
    
    # 对外提供的对象
    song = Songs()
    
    #外界通过导包拿到这个对象都是一样的
    from part1.SingleModule import song
    print(song)
    
    from part1.SingleModule import song
    print(song)
    
    from part1.SingleModule import song
    print(song)

       方式2:用类方法来获取唯一对象

    class Songs():
        __instance = None
        @classmethod
        def getInstance(cls):
            # 对象没有创建返回,有直接返回
            if cls.__instance == None:
                cls.__instance = cls()
            #把第一次实例化对象的内存地址传给类__instance属性
            return cls.__instance
    
    s1 = Songs.getInstance()
    s2 = Songs.getInstance()
    print(s1, s2)

      方式3:利用obj.__new__方法:

    class Songs:
    __instance = None
    def __new__(cls, song_name, *args, **kwargs):
    if cls.__instance == None:
    cls.__instance = object.__new__(cls)
    cls.__instance.song_name = song_name
    #先利用object.__new__开辟了一个对象内存空间,然后赋予属性实例化
    return cls.__instance
    #通过类方法可以修改属性值
    def change_song(self, song_name):
    self.song_name = song_name

    s1 = Songs('菊花台')
    s2 = Songs('东风破')
    print(s1.song_name, s2.song_name) # 菊花台 菊花台
    s2.change_song('青花瓷')
    print(s1.song_name, s2.song_name) # 青花瓷 青花瓷

    方式4.装饰器完成单例:

    def outer(cls):
        _instance = None
        def inner(*args, **kwargs):
            nonlocal _instance
            if _instance == None:
                _instance = cls(*args, **kwargs)
            return _instance
        return inner
    
    @outer  # Songs = outer(Songs)
    class Songs:
        pass
    
    
    s1 = Songs()
    s2 = Songs()
    print(s1, s2)

    方式5.通过元类完成单例:

    class SingleMeta(type):
        __instance = None
        def __call__(cls, *args, **kwargs):
            if SingleMeta.__instance == None:
                SingleMeta.__instance = object.__new__(cls)
                cls.__init__(SingleMeta.__instance, *args, **kwargs)
            return SingleMeta.__instance
    
    
    class Songs(metaclass=SingleMeta):
        def __init__(self):
            pass
        pass
    
    
    s1 = Songs()
    s2 = Songs()
    print(s1, s2)
     
  • 相关阅读:
    MySQL性能优化
    mysql中OPTIMIZE TABLE的作用
    Linux中/usr与/var目录详解
    Mysql之EXPLAIN显示using filesort
    MySQL ALTER语法的运用方法 && 操作索引和字段
    NoSQL数据库的分布式算法&&memcache集群的实现
    linux用户权限
    hdoj1241 Oil Deposits
    ny42 一笔画问题
    ny20 吝啬的国度
  • 原文地址:https://www.cnblogs.com/sima-3/p/11058418.html
Copyright © 2011-2022 走看看