元类

globals()函数

python中 globals() 函数可以查看一个内存空间 它以字典的形式存储了变量名 和变量的引用 只有在这个空间里有的变量 才可以使用 这个内存空间中储存了所有定义的全局变量以及类对象 实例对象 的引用以及所有不需要定义就可以使用的函数 如: print() next() list() 都在内建模块__builtin__中 当使用这些函数时 程序会在globals()所查看的空间中查找 如果没有就在内建模块中查找 如果还没有找到才会报错
当使用class 类名 定义类时 实际上就已经在这个空间中定义了一个对象key是类名 指向一个空间 所以类也是一个对象

元类

类实际上是一个字典 类的空间中以key的形式储存了变量名 指向一个globals()的空间中的(类属性) 列表(类属性) 或者函数(方法)或者指向叫做__init__的函数(实例属性)

使用type()创建类

class A(object):  # 以下要继承的父类
    num = 100

def print_b(self):  # 普通函数
    print(self.num)

@staticmethod
def print_static():  # 带装饰器的函数
    print("----haha-----")

@classmethod
def print_class(cls):
    print(cls.num)

# 此时使用type 创建的类 有父类 类方法 静态方法 实例方法
# 这个类在globals()的空间中用变量B来存储 而这个类叫做C
B = type("C", (A,), {"print_b": print_b, "print_static": print_static, "print_class": print_class})

a = A()
b = B()
b.print_b()  # 结果====> 100
b.print_static()  # 结果====> ----haha-----
b.print_class()  # 结果====> 100

#  实例对象由类创建 而不管是class的方式还是type的方式创建的类对象 都是由type创建的
b.__class__  # 结果====> __main__.C
b.__class__.__class__  # 结果====> type
a.__class__  # 结果====> __main__.A
a.__class__.__class__  # 结果====> type

#  函数由function 创建 而创建function的 也是type
print_b.__class__  # 结果====> function
print_b.__class__.__class__  # 结果====> type

类与实例对象 都在这个空间中 而类与实例对象的区别在于 类有创建实例对象的功能而实例对象没有创建对象的功能
创建类对象的东西就是元类 元类不仅创建类对象 所有的对象最根本都是元类type创建的

__metaclass__属性

在定义一个类的时候为其添加__metaclass__属性

class Foo(Bar, metaclass=something):  # python3写法
    # python2 写法
    # __metaclass__ = something…
    pass
  • Python会通过__metaclass__指定的对象创建一个名字为Foo的类(对象)
  • 如果Python没有找到__metaclass__ 会继续在Bar(父类)中寻找__metaclass__属性 并尝试做和前面同样的操作
  • 如果Python在任何父类中都找不到__metaclass__ 它就会在模块层次中去寻找__metaclass__ 并尝试做同样的操作
  • 如果还是找不到__metaclass__Python就会用内置的type来创建这个类对象

可以在__metaclass__中放置可以创建一个类的东西: type 或者任何使用到type的东西都可以

自定义元类

将所有小写属性名变为大写

使用函数:
#-*- coding:utf-8 -*-
def upper_attr(class_name, class_parents, class_attr):

    #遍历属性字典,把不是__开头的属性名字变为大写
    new_attr = {}
    for name,value in class_attr.items():
        if not name.startswith("__"):
            new_attr[name.upper()] = value

    #调用type来创建一个类
    return type(class_name, class_parents, new_attr)

class Foo(object, metaclass=upper_attr):
    bar = 'bip'

print(hasattr(Foo, 'bar'))  # False
print(hasattr(Foo, 'BAR'))  # True

f = Foo()
print(f.BAR)
使用真正的class来当做元类:
#coding=utf-8
class UpperAttrMetaClass(type):
    # __new__ 是在__init__之前被调用的特殊方法
    # __new__是用来创建对象并返回之的方法
    # 而__init__只是用来将传入的参数初始化给对象
    # 你很少用到__new__,除非你希望能够控制对象的创建
    # 这里,创建的对象是类,我们希望能够自定义它,所以我们这里改写__new__
    # 如果你希望的话,你也可以在__init__中做些事情
    # 还有一些高级的用法会涉及到改写__call__特殊方法,但是我们这里不用
    def __new__(cls, class_name, class_parents, class_attr):
        # 遍历属性字典,把不是__开头的属性名字变为大写
        new_attr = {}
        for name, value in class_attr.items():
            if not name.startswith("__"):
                new_attr[name.upper()] = value

        # 方法1:通过'type'来做类对象的创建
        return type(class_name, class_parents, new_attr)

        # 方法2:复用type.__new__方法
        # 这就是基本的OOP编程,没什么魔法
        # return type.__new__(cls, class_name, class_parents, new_attr)

# python3的用法
class Foo(object, metaclass=UpperAttrMetaClass):
    bar = 'bip'

# python2的用法
# class Foo(object):
#     __metaclass__ = UpperAttrMetaClass
#     bar = 'bip'


print(hasattr(Foo, 'bar'))
# 输出: False
print(hasattr(Foo, 'BAR'))
# 输出:True

f = Foo()
print(f.BAR)
# 输出:'bip'

继承type的类就可以简单的认为是一个元类
子类的类对象内存空间是由父类的__new__方法来创建的
但是在metaclass指定元类后 这个类的空间就由原来的父类变为由元类__new__方法来创建
修改元类中__new__接收到的值实现修改类
直接返回type(.....)实现完成类对象创建 或者 返回父类type.__new__(cls, .....)的方法实现完成创建类对象的内存空间

元类的作用:

  • 拦截类的创建
  • 修改类
  • 返回修改之后的类
    装饰器可以给函数添加功能, 元类可以给添加功能

元类实现ORM

ORM 是 python编程语言后端web框架 Django的核心思想 “Object Relational Mapping” 即对象-关系映射 简称ORM
一个句话理解就是:创建一个实例对象 用创建它的类名当做数据表名 用创建它的类属性对应数据表的字段 当对这个实例对象操作时能够对应相应的MySQL语句


ORM.png

通过元类实现ORM insert功能

class ModelMetaclass(type):
    def __new__(cls, name, bases, attrs):
        mappings = dict()
        # 判断是否需要保存
        for k, v in attrs.items():
            # 判断是否是指定的StringField或者IntegerField的实例对象
            if isinstance(v, tuple):
                print('Found mapping: %s ==> %s' % (k, v))
                mappings[k] = v

        # 删除这些已经在字典中存储的属性
        for k in mappings.keys():
            attrs.pop(k)

        # 将之前的uid/name/email/password以及对应的对象引用、类名字
        attrs['__mappings__'] = mappings  # 保存属性和列的映射关系
        attrs['__table__'] = name  # 假设表名和类名一致
        return type.__new__(cls, name, bases, attrs)


class User(metaclass=ModelMetaclass):
    uid = ("int", "unsigned")
    name = ("varchar(30)","not null")
    email = ("varchar(30)", "not null")
    password = ("varchar(30)", "not null")
    # 当指定元类之后,以上的类属性将不在类中,而是在__mappings__属性指定的字典中存储
    # 以上User类中有 
    # __mappings__ = {
    #     "uid": ('uid', "int unsigned")
    #     "name": ('username', "varchar(30)")
    #     "email": ('email', "varchar(30)")
    #     "password": ('password', "varchar(30)")
    # }
    # __table__ = "User"
    def __init__(self, **kwargs):
        for name, value in kwargs.items():
            setattr(self, name, value)

    def save(self):
        fields = []
        args = []
        for k, v in self.__mappings__.items():
            fields.append(k)
            args.append(getattr(self, k)

        args_temp = list()
        for temp in args:
            # 判断入如果是数字类型
            if isinstance(temp, int):
                args_temp.append(str(temp))
            elif isinstance(temp, str):
                args_temp.append("""'%s'""" % temp)
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(args_temp))
        print('SQL: %s' % sql)


u = User(uid=12345, name='Michael', email='test@orm.org', password='my-pwd')
# print(u.__dict__)
u.save()

抽取到基类中

class ModelMetaclass(type):
    def __new__(cls, name, bases, attrs):
        mappings = dict()
        # 判断是否需要保存
        for k, v in attrs.items():
            # 判断是否是指定的StringField或者IntegerField的实例对象
            if isinstance(v, tuple):
                print('Found mapping: %s ==> %s' % (k, v))
                mappings[k] = v

        # 删除这些已经在字典中存储的属性
        for k in mappings.keys():
            attrs.pop(k)

        # 将之前的uid/name/email/password以及对应的对象引用、类名字
        attrs['__mappings__'] = mappings  # 保存属性和列的映射关系
        attrs['__table__'] = name  # 假设表名和类名一致
        return type.__new__(cls, name, bases, attrs)


class Model(object, metaclass=ModelMetaclass):
    def __init__(self, **kwargs):
        for name, value in kwargs.items():
            setattr(self, name, value)

    def save(self):
        fields = []
        args = []
        for k, v in self.__mappings__.items():
            fields.append(k)
            args.append(getattr(self, k)

        args_temp = list()
        for temp in args:
            # 判断入如果是数字类型
            if isinstance(temp, int):
                args_temp.append(str(temp))
            elif isinstance(temp, str):
                args_temp.append("""'%s'""" % temp)
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(args_temp))
        print('SQL: %s' % sql)


class User(Model):
    uid = ("int", "unsigned")
    name = ("varchar(30)","not null")
    email = ("varchar(30)", "not null")
    password = ("varchar(30)", "not null")


u = User(uid=12345, name='Michael', email='test@orm.org', password='my-pwd')
# print(u.__dict__)
u.save()

如果在类中没有发现metaclass会在父类中寻找metaclass 父类中的metaclass指定了元类

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 官网 中文版本 好的网站 Content-type: text/htmlBASH Section: User ...
    不排版阅读 4,533评论 0 5
  • 元类 类就是一组用来描述如何生成一个对象的代码段 类也是对象,你可以在运行时动态的创建它们,就像其他任何对象一样 ...
    等哈光圈阅读 616评论 0 1
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,170评论 1 32
  • 一、温故而知新 1. 内存不够怎么办 内存简单分配策略的问题地址空间不隔离内存使用效率低程序运行的地址不确定 关于...
    SeanCST阅读 7,937评论 0 27
  • 十一:函数和函数式编程 11.1 什么是函数? 函数是对程序逻辑进行结构化或过程化的一种编程方法。能将整块代码巧妙...
    m风满楼阅读 448评论 0 0