29.Python中的元类

作者: TensorFlow开发者 | 来源:发表于2018-07-09 17:16 被阅读0次

    回顾

    前面在学习Python编程:获取对象信息知识时,我们已经知道了type()函数。这个type()函数既可以返回一个对象的类型,又可以创建出新的类型。type()函数在传入一个参数时,返回的是对象所属的类型。这里特别要注意的是:通常所说的类比如动物类Animal、狗类Dog、猫类Cat、汽车类Car等,都是type的实例,说白了就是:动物类Animal、狗类Dog、猫类Cat、汽车类Car等,这些类的类型都是type类型。

    type()函数当传入三个参数时,可以动态创建新的class对象,也就是新类型。

    一个入参:获取类型type(obj)

    class type(obj)官方解释:

    With one argument, return the type of an object. The return value is a type object and generally the same object as returned by object.__class__.

    意思大概是说:type函数传入一个参数时,返回结果是该对象的类型。返回值是type对象,和调用该对象的__class__方法返回值一样。

    三个入参:动态创建新的类实例 class type(name, bases, dict)

    type()函数当传入三个参数时,可以动态创建新的class对象,也就是新类型,如:通过该函数动态创建Fox类。 class type(name, bases, dict)的官方解释如下:

    With three arguments, return a new type object. This is essentially a dynamic form of the class statement. The name string is the class name and becomes the__name__attribute; the bases tuple itemizes the base classes and becomes the __bases__ attribute; and the dict dictionary is the namespace containing definitions for class body and is copied to a standard dictionary to become the__dict__attribute. For example, the following two statements create identical type objects。

    要创建一个class对象,type()函数依次传入3个参数:
    1.新class的名称;
    2.所要继承的父类的集合,注意Python支持多重继承,如果只有一个父类,别忘了tuple的单元素写法;
    3.新class中的方法名称与函数可通过字典键值对一一绑定。例如:此参数传入:dict(my_eat=eat)这里我们把函数eat绑定到方法名my_eat上。

    通过type()函数创建的类和直接写class是完全一样的,因为Python解释器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()函数创建出class(即通常所说的类,这里类也是一个个的实例,类是type类型)。

    看到这里,你会发现这与Java中的反射机制类似的:运行时动态创建的对象、绑定属性、方法等。

    示例

    我们说class的定义是运行时动态创建的,而创建class的方法就是使用type()函数。比如,我们可以通过type()函数创建出Fox类实例,而无需通过class Fox(object)...的定义:

    def my_eat(obj):
        print("吃东西")
    
    # 用type()创建类实例
    Fox = type("Fox", (object,), dict(eat=my_eat))
    
    f = Fox()
    
    f.eat()
    

    运行结果:

    吃东西
    

    正常情况下,我们都用class A...来定义类,但是,type()函数也允许我们动态创建出类来,也就是说,动态语言本身支持运行期动态创建类,这和静态语言有非常大的不同,要在静态语言运行期创建类,必须构造源代码字符串再调用编译器,或者借助一些工具生成字节码实现,本质上都是动态编译,会非常复杂。

    metaclass

    除了使用type()动态创建类以外,要控制类的创建行为,还可以使用metaclass。

    metaclass,直译为元类,简单的解释就是:当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。

    但是如果我们想创建出类呢?那就必须根据metaclass创建出类,所以:先定义metaclass,然后创建类。

    连接起来就是:先定义metaclass,就可以创建类,最后创建实例。

    所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的“实例”。

    metaclass是Python面向对象里最难理解,也是最难使用的魔术代码。正常情况下,你不会碰到需要使用metaclass的情况,所以,以下内容看不懂也没关系,因为基本上你不会用到。

    示例

    们先看一个简单的例子,这个metaclass可以给我们自定义的MyList增加一个add方法:

    定义ListMetaClass,按照默认习惯,metaclass的类名总是以Metaclass结尾,以便清楚地表示这是一个metaclass:

    # 定义元类ListMetaClass。metaclass是类的模板,所以必须从`type`类型派生:
    class ListMetaClass(type):
        def __new__(cls, name, bases, attrs):
            attrs["add"] = lambda self, value: self.append(value)
            return type.__new__(cls, name, bases, attrs)
    
    # 自定义MyList类
    class MyList(list, metaclass=ListMetaClass):
        pass
    
    my_list = MyList()
    my_list.add(2)
    my_list.add(4)
    print(my_list)
    

    运行结果:

    [2, 4]
    

    我们在定义MyList类的时候还要指示使用ListMetaClass来定制类,正是通过传入关键字参数metaclass来指定的:class MyList(list, metaclass=ListMetaClass):
    当我们传入关键字参数metaclass时,魔术就生效了,它指示Python解释器在创建MyList时,要通过ListMetaclass.new()来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,返回修改后的定义。

    __new__()方法接收到的参数依次是:

    • 当前准备创建的类的对象;
    • 类的名字;
    • 类继承的父类集合;
    • 类的方法集合。

    小结

    本结知识,是python中生涩的知识点,难理解还几乎用不到,只做理解即可。


    更多了解,可关注公众号:人人懂编程


    微信公众号:人人懂编程

    相关文章

      网友评论

        本文标题:29.Python中的元类

        本文链接:https://www.haomeiwen.com/subject/zrltpftx.html