zoukankan      html  css  js  c++  java
  • 元类的理解

    回忆一下之前type的介绍

    class Foo(object):
      
      
        def __init__(self,name):
            self.name = name
      
      
    f = Foo("lxj")
    
    print( type(f)) # 输出:<class '__main__.Foo'>     表示,obj 对象由Foo类创建
    print( type(Foo)) # 输出:<type 'type'>              表示,Foo类对象由 type 类创建
    

      可以得出f对象是Foo类的一个实例Foo类是 type 类的一个实例,即:Foo类 是通过type类创建的实例。type就是一个类的类,我们也称为元类

    由于元类是一个类的类,所以它被用来构造类(就像一个类用于构造对象一样)。但是等一下,我们不是创建一个具有标准类定义的类吗?当然,但是Python在底下做了什么:

    当它看到一个类定义时,Python会执行它来收集字典中的属性(包括方法)。
    当类定义结束时,Python确定类的元类。我们称之为Meta
    最终,Python执行Meta(name,base,dct),其中:
    Meta是元类,所以这个调用正在实例化它。
    name是新创建的类的名称
    base:类的基类的一个元组
    dct将属性名称映射到对象,列出所有类的属性
    我们如何确定一个类的元类?简单地说,如果一个类或它的一个基类有一个__metaclass__属性,它就被当作元类。否则,类型是元类。

    当我们定义一个

    class MyKlass(object):
      foo = 2
    

      在这个例子中,没有__metaclass__属性,所以就用type代替,创建过程像下面这样

    MyKlass = type(name, bases, dct)
    

      如果metaclass被定义

    class MyMeta():
        pass
    class MyKlass(object,metaclass=MyMeta):  # python3写法
    
        foo = 2
    

      创建过程就像这样

    MyKlass = MyMeta(name, bases, dct)
    

      

    __new__ :当你想要控制一个新的对象的创建(在我们的例子中是类)

    __init__ :应该在创建新对象后控制新对象的初始化时执行。

    MyKlass = MyMeta.__new__(MyMeta, name, bases, dct)
    MyMeta.__init__(MyKlass, name, bases, dct)
    

     看个例子 

    class MyMeta(type):
        def __new__(meta, name, bases, dct):
            print('-----------------------------------')
            print ("Allocating memory for class", name)
            print ("meta>>",meta)   #通过程序meta:<class '__main__.MyMeta'>
            print (bases)
            print (dct)
    
    
            return super(MyMeta, meta).__new__(meta, name, bases, dct)
        def __init__(cls, name, bases, dct):
            print ('-----------------------------------')
            print ("Initializing class", name)   
            print ("cls>>",cls)    #cls:<class '__main__.MyKlass'>
            print (bases)
            print (dct)
    
            super(MyMeta, cls).__init__(name, bases, dct)
    
    class MyKlass(object,metaclass=MyMeta):
        # __metaclass__ = MyMeta
    
        def foo(self, param):
            pass
    
        barattr = 2
    

      

     我们使用pycharm的debug模式来看下程序的运行顺序:

    1、解释器从上到下执行,class MyMeat - __new__ - __init__ - class MyKclass -def foo -barattr  我的理解是依次获得函数或则属性的内存地址

    2、检测到有metaclass字段,运行__new__ ,__init__

    有调试结果得出来,当有metaclass字段时,当解释器编译完所有代码时,会回过头再执行metaclass指向的类中的__new__和__init__方法

    用元类实现一个单例模式:

    当我们创建一个类时,每次实例化都是一个新的内存地址,看代码

    class Spam(object):
        def __init__(self):
            print("creating Spam")
    
    s = Spam()
    a =Spam()
    print(s is a )
    #结果为False
    

     再看用元类实现单例模式:

    class Singleton(type):
        def __init__(cls,*args,**kwargs): #cls类
            print("Singleton __init__")
            print(cls,*args,*kwargs)
            cls.__instance = None
            super().__init__(*args,**kwargs)
    
        def __call__(cls, *args, **kwargs):
    
            print("Singleton __call__")
            print(cls)
            if cls.__instance is None: #判断实例是否被创建
                cls.__instance = super().__call__(*args,**kwargs)
                print(cls.__instance)
                return cls.__instance
            else:
                return cls.__instance
    
    class Spam(metaclass=Singleton):
        def __init__(self,name):
            print("creating Spam name is ",name)
    # sp= Singleton(1)
    s = Spam('lxj')
    a =Spam('sx')
    print(s is a )
    
    结果:
    Singleton __init__
    <class '__main__.Spam'> Spam () {'__module__': '__main__', '__init__': <function Spam.__init__ at 0x7fc0096ff8c8>, '__qualname__': 'Spam'}
    Singleton __call__
    <class '__main__.Spam'>
    creating Spam name is  lxj
    <__main__.Spam object at 0x7fc00970dcf8>
    Singleton __call__
    <class '__main__.Spam'>
    True
    

      

    1、首先在实例化前,因为有metaclas存在,我们调用了__init__函数(参考上面的解释),把cls._instance置为None(即还未实例化),这里cls为Spam,为什么不用__new__方法,因为__new__方法中cls为Singleton。上面的知识点我们也知道__init__方法是用在控制新对象的初始化

    2、在结果中我们看到,在我们实例化过程中,调用的是__call__,每实例话一次,就调用一次__call__方法,在call方法中对实例化进行控制

    3、结果中我们看到a和s为指向同一内存地址

     

    在学到django时再仔细深入https://github.com/inglesp/Metaclasses

    https://stackoverflow.com/questions/100003/what-are-metaclasses-in-python

  • 相关阅读:
    meta标签
    Vue(day8)
    Vue(day7)
    Vue(day6)
    Flex布局
    Vue(day5)
    jquery.data()&jquery.extend()
    Promise对象
    Vue(day4)
    Vue(day3)
  • 原文地址:https://www.cnblogs.com/zj-luxj/p/7881320.html
Copyright © 2011-2022 走看看