元编程

元编程概述

一般写程序就是直接写代码,能否用代码生成所需的代码?例如,写了一个类 class A,能否用代码生成一个类出来?

用来生成代码的程序称为元程序 metaprogram,编写这种程序就称为元编程 metaprogramming。

python能通过反射实现元编程。在python中:

  • 所有非object类都继承自object类;
    • 在 Python 中,所有的类都可以追溯到 object 类,这意味着 object 类是所有类的基类。这种继承关系意味着所有类都继承了 object 类的属性和方法。
  • 所有类的类型包括type类都是type;
    • 在 Python 中,类本身也是对象,它们的类型是 type。这包括了普通的类以及 type 类本身。
  • type类继承自object类,object类的类型也是type类。
    • 这意味着 type 类本身也是一个对象,它继承了 object 类的属性和方法。同时,object 类的类型也是 type 类,因为 object 类是一个类,而类的类型是 type。

这些概念在元编程中非常重要,因为它们揭示了类和对象之间的关系,以及如何使用反射和元编程技术来操作类和对象。

Python中的元编程是指在运行时创建或修改Python代码的能力。这种技术允许程序在运行时动态地生成类、函数、方法和模块,或者在运行时修改它们的行为。元编程通常用于创建通用框架、装饰器、插件系统等。

Python提供了几种元编程的机制,包括:

  1. 元类(Metaclasses):元类是用于创建类的类。通过定义自己的元类,您可以控制类的创建过程。元类的 __new____init__ 方法可以用来拦截类的创建过程,并且允许您在类创建时进行自定义操作。

  2. 装饰器(Decorators):装饰器是用于修改函数、方法或类的行为的函数。它们允许您在不修改原始对象的情况下,动态地添加额外的功能。装饰器是Python元编程中常用的技术之一,用于实现日志记录、性能分析、权限检查等功能。

  3. 类装饰器(Class Decorators):类装饰器是用于修改类行为的装饰器。它们允许您在类定义时对类进行修改或增强。类装饰器可以用于实现单例模式、缓存类实例等功能。

  4. 元编程模块(Metaprogramming modules):Python标准库中的一些模块提供了元编程的支持,如 inspect 模块用于检查对象的属性和源代码,ast 模块用于操作抽象语法树(AST),types 模块用于动态创建新类型等。

  5. 动态属性和方法:Python允许在运行时动态地添加、修改或删除对象的属性和方法。这种灵活性使得在运行时根据需要动态地修改对象成为可能,这对于某些特定的应用场景非常有用。

元编程是Python中非常强大且灵活的一部分,但也需要谨慎使用,因为它可以使代码更加复杂和难以理解。优秀的元编程代码应该具有清晰的文档和易于理解的设计。

元编程通常与面向对象编程(Object-Oriented Programming,OOP)概念相关联,因为它涉及到在运行时创建、修改或操作类、对象和其行为。

在面向对象编程中,类是对象的蓝图,它定义了对象的属性和方法。元编程提供了一种在程序运行时动态创建、修改或扩展这些类和对象的能力,从而增强了面向对象编程的灵活性和可扩展性。

通过元编程,您可以在运行时创建新的类,添加新的方法或属性,或者修改现有类的行为,而无需在代码编写阶段静态定义它们。这使得代码可以更具动态性,并且可以根据需要进行自我适应或扩展。因此,尽管元编程是一个独立的概念,但通常与面向对象编程一起使用,以提供更灵活和强大的编程范式。

type 类

type是元类,是构造其他类的类。

在 Python 中,type 是一个内置的元类,它用于创建 Python 中的所有类。type 类本身也是一个类,同时也是自身的实例。

以下是关于 type 类的一些详解:

  1. 创建类对象:使用 type 类可以动态地创建类对象。通常,我们可以使用类定义语法来创建类,但是 type 类提供了一种以编程方式创建类的方式。例如,下面的代码使用 type 类创建了一个名为 MyClass 的类:

    MyClass = type('MyClass', (), {})

    这行代码的作用等同于下面的类定义语法:

    class MyClass:
        pass
  2. 类的元类type 本身也是一个元类,用于创建其他类的类。在 Python 中,所有的类都是 type 类的实例,包括 type 类本身。这种特性使得 type 成为 Python 中元编程的基础。

  3. type 的构造函数type 类具有构造函数的功能。其签名如下:

    type(name, bases, dict)
    • name 是要创建的类的名称。
    • bases 是一个元组,表示新类的基类。如果新类没有基类,则应传入一个空元组 ()
    • dict 是一个字典,包含新类的属性和方法。
  4. 探索类的信息type 类也提供了一些方法来探索类的信息,例如 type.__name__ 可以获取类的名称,type.__bases__ 可以获取类的基类。

  5. 元类的自定义:通过继承 type 类并覆盖其方法,可以自定义元类,从而控制类的创建过程和行为。这种能力使得 Python 中的元编程变得非常灵活。

总的来说,type 类在 Python 中具有重要的地位,它是类创建的基础,也是元编程的核心。通过了解和使用 type 类,可以更深入地理解 Python 中的类和对象模型,以及如何利用元编程来实现更加灵活和动态的代码结构。

示例1

## type(name, bases, dict, **kwds) -> a new type
XClass = type('X', (), {}) # x是名字,给人看的。XClass是标识符,给程序员用的。
print(XClass) # <class '__main__.X'>
print(type(XClass)) # <class 'type'>
print(XClass.__name__) # X

等价于:

class YClass: pass # 可以理解为这是一种通过语法糖的创建方式。通过这种方式定义,标识符和name同名
print(YClass) # <class '__main__.YClass'>
print(type(YClass)) # <class 'type'>
print(YClass.__name__) # YClass

示例2

## type(name, bases, dict, **kwds) -> a new type
XClass = type('X', (object, ), {})
print(XClass.__bases__) # (<class 'object'>,)

等价于:

class YClass(object): pass
print(YClass.__bases__) # (<class 'object'>,)

示例3

## type(name, bases, dict, **kwds) -> a new type
XClass = type('X', (list, ), {})
print(XClass.__bases__) # (<class 'list'>,)
a = XClass()
print(a) # []
a.append(1)
a.extend(range(10, 15))
print(a) # [1, 10, 11, 12, 13, 14]

示例4

def a(self):
    print('init~~~')
    self.x = 1000

def show(self):
    print(self.x)

XClass = type('X', (list, ), {'a': 123, 'b': 'abc', '__init__': a, 'show': show})

print(XClass.__dict__) # {'a': 123, 'b': 'abc',...

x = XClass()
print(x)
'''
init~~~
[]
'''
print(x.__dict__) # {'x': 1000}
x.show() # 1000

等价于:

class XClass(list):
    a = 123
    b = 'abc'

    def __init__(self):
        print('init~~~')
        self.x = 1000

    def show(self):
        print(self.x)


print(XClass.__dict__) # {'a': 123, 'b': 'abc',...

x = XClass()
print(x)
'''
init~~~
[]
'''
print(x.__dict__) # {'x': 1000}
x.show() # 1000

示例5

实际应用1

class ModelMeta(type):
    def __new__(cls, name, bases, attrs):
        print('Meta ~~~')
        return super().__new__(cls, name, bases, attrs)

class Model(metaclass=ModelMeta):
    pass

class Student(Model):
    pass

## 输出两次:
'''
Meta ~~~
Meta ~~~
'''

实际应用2

class Field:
    def __init__(self, fieldname=None, pk=False, nullable=False):
        self.fieldname = fieldname
        self.pk = pk
        self.nullable = nullable

class ModelMeta(type):
    def __new__(cls, name, bases, attrs):
        print('Meta ~~~')
        return super().__new__(cls, name, bases, attrs)

class Model(metaclass=ModelMeta):
    pass

class Student(Model):
    id = Field(pk=True, nullable=False)
    name = Field(fieldname='username', nullable=False)
    age = Field(nullable=True)

    def __repr__(self):
        return '<Field {{} {} {}}>'.format(
            self.id, self.name, self.age
        )

    __str__ = __repr__

实际应用3

class Field:
    def __init__(self, fieldname=None, pk=False, nullable=False):
        self.fieldname = fieldname
        self.pk = pk
        self.nullable = nullable

    def __repr__(self):
        return '<Field {} {} {}>'.format(
            self.fieldname, self.pk, self.nullable
        )

    __str__ = __repr__


class ModelMeta(type):
    def __new__(cls, name, bases, attrs):
        print('Meta ~~~')
        if name != 'Model':
            primarykeys = []
            print(name)
            print(bases)
            print(attrs)
            if 'db_table' not in attrs:
                print('-' * 30)
                attrs['db_table'] = name
            for k, v in attrs.items():
                # print(f"Key: {k}, Value: {v}")
                if isinstance(v, Field):
                    if not v.fieldname or v.fieldname.strip() == '':
                        v.fieldname = k
                    if v.pk:
                        primarykeys.append(v)
            attrs['primarykeys'] = primarykeys
            print(attrs)
        return super().__new__(cls, name, bases, attrs)


class Model(metaclass=ModelMeta):
    pass


class Student(Model):
    id = Field(pk=True, nullable=False)
    name = Field(fieldname='username', nullable=False)
    age = Field(nullable=True)


print('=' * 30)
print(Student.__dict__)
s1 = Student()
print(s1.db_table)

元编程总结

元类是制造类的工厂,是用来构造类的类。

构造好元类,就可以在类定义时,使用关键字metaclass指定元类,可以使用最原始的metatype(name, bases, dict)的方式构造一个类。

元类的__new__()方法中,可以获取元类信息、当前类、基类、类属性字典。

元编程一般用于框架开发中。

Django、SQLAlchemy使用了元类,让我们使用起来很方便。

注意:

  • 开发中除非你明确的知道自己在干什么,否则不要随便使用元编程;
  • 99%的情况下用不到元类,可能有些程序员一辈子都不会使用元类。