元编程


    physics,即物理学,是研究物质、能量的本质与性质,以及它们彼此之间相互作用的自然科学。
    metaphysics, 被翻译为「形而上学」,由日本人 井上哲次郎 于 明治时代 翻译为汉字词语,指研究事物本身的学问。通常人们在谈论形而上学时,往往将它作为一种哲学上的讨论--关于根本(或者存在)本身的意义。

    meta前缀,源于希腊语,其本意是「在…后,越过…的」,而后在西方哲学界的发展中渐渐赋予该词缀一种全新的意义:关于某事自身的某事。比如 meta-knowledge 就是「关于知识本身的知识」,meta-data 就是「关于数据的数据」,meta-language 就是「关于语言的语言」,而 meta-programming 也是由此而来,是「关于编程的编程」。

meta在中国大陆多被翻译为「元」,在台湾多被翻译为「后设」

什么是元编程

元编程的目的是操作其他的程序(或自身)作为它自身的数据,在运行时完成它本应该在编译时应该完成的任务。

一个使用bash脚本描述的元程序


#!/bin/bash
# metaprogram
echo '#!/bin/bash' >program
for ((I=1; I<=992; I++)) do
    echo "echo $I" >>program
done
chmod +x program

元编程的能力在很大的程度上也和选择的语言有关系,比如:C/C++ 的宏, Java的反射和动态代理……
而像Python这种动态性强的语言,给对象加个属性、方法啥的,简直不值一提。

一切都是对象……吗?

继在(类)Unix世界喊出了「一切皆是文件」的口号之后,Python世界也喊出了「一切皆是对象」的口号。

不信?打开终端,随便输入点什么,来看看结果


>>> type("a")

>>> type(1)

>>> type([])

>>> type({})

>>> type(Exception())

>>> type(object())


如果你有一个自定义的类Person, 就会有


>>> EvinK = Person()
>>> type(EvinK)


上面所有的东西,都被type()识别为某个class(对象)的实例。

对象的对象

Python世界里所有的对象都继承于object对象,使用isinstance(instance, object)就可以验证这一点。但是,object又是什么类型呢?按照直觉的话,它应该还是一个object类型,多说无益,开一个终端来测试一下吧!


>>> type(object)

>>> type(type)

>>> isinstance(object, object)
True
>>> isinstance(object, type)
True
>>> isinstance(type, object)
True
>>> isinstance(type, type)
True

原来,object 是一个 type 类型。

type 不光作为一个类型鉴定函数,居然是一个类。那既然是类,肯定也 object 的子类,但是同时, objecttype 的一个实例,于是间接地 object 也成为了自身的实例(由于 typeobject 的继承关系)。

元编程_第1张图片
python-type-instance.png

由此推论,所有继承于 object 的类(即Python中的所有对象),都是 type 的一个实例。

type 在此就作为这些类的类之存在,并用来定义这些类中的属性的方法。它是Python世界里最为特殊的一个对象,一个用来操纵对象的对象。它是 object 的子类, object 是它的实现。

使用type来定义一个类

一个已知的定义类的方式(如下),已经写入了各种文章中


class Person:
    """
    Person Document
    """
    # 类变量
    name = "EvinK"
    # 私有类变量
    __private_var = 1

    # 实际上的构造函数
    def __new__(cls, *args, **kwargs):
        cls.age = kwargs["age"]
        return cls

    # 此方法将不会被执行
    def __init__(self, age):
        self.age_ = age

    # 静态方法
    @staticmethod
    def get_private_var():
        return Person.__private_var

    # 类方法
    @classmethod
    def get_last_age(cls):
        # 返回最后一个实例的age
        return cls.age

if __name__ == "__main__":

    print(Person.__dict__)

    {
        '__module__': '__main__',
        '__doc__': '\n    Person Document\n    ',
        'name': 'EvinK',
        '_Person__private_var': 1,
        '__new__': ,
        '__init__': ,
        'get_private_var': ,
        'class_method': ,
        '__dict__': ,
        '__weakref__': 
    }

    evink = Person(age=13)
    print(evink.__dict__)

    {
        '__module__': '__main__',
        '__doc__': '\n    Person Document\n    ',
        'name': 'EvinK',
        '_Person__private_var': 1,
        '__new__': ,
        '__init__': ,
        'get_private_var': ,
        'class_method': ,
        '__dict__': ,
        '__weakref__': ,
        'age': 13
    }

上述的类被一个字典给描述,其中包含有这个类的类变量 , 静态方法类方法 , 方法构造方法 。而类的实例也可以被一个字典描述,只不过多了写 实例变量 而已。这两者的结构看上去是如此的相似,但是一个是类型(type的实例),一个是(类的)实例。

既然通过类的构造方法就可以生成实例,那作为 type 实例的类,是不是也可以由 type 生成呢?


Person = type("Person", (), dict(
        name="EvinK",
        # 使用type生成的类声明将不会被转换为 _Person__private_var 这种形式
        __private_var=1
    ))

Person.__doc__ = \
        """
            Person
        """

@staticmethod
def new(cls, *args, **kwargs):
        return cls

# 出现__new__方法时, 此构造将不会被执行
def init(self, age):
        self.age = age

Person.__init__ = init
Person.__new__ = new

上面由type生成的类,使用 __dict__ 属性时,可以看到,基本和我们使用 class 关键字声明的类长得一样。这对某些静态类型的语言来说简直是天方夜谭!

未完待续……

原文地址: https://code.evink.me/2018/09/post/Meta-Programming/

你可能感兴趣的:(元编程)