小白尝试写一篇类元编程记录。

Java学了几个小时,这两天又被元编程搞死,准备粗粗写一些我的理解。后面还有协程需要理解。感觉年底之前搞定这些有点累。

先上参考文献:https://www.liaoxuefeng.com/wiki/1016959663602400/1017592449371072

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

https://www.jianshu.com/p/224ffcb8e73e

觉的我写的垃圾的,可以看链接的,都试大神级别写的。

首先,我们在定义一个普通的类的时候,一般都用class,后面一个类名就可以了,但Python万物皆对象,那我们的类又是谁创造的呢?或者说我们的类是哪个类爸爸实例出来的。

In [332]: class Demo:
     ...:     pass
     ...:                                                                                                     

In [333]: Demo.__class__
Out[333]: type

从代码可以明显看出来,类是由类爸爸创建的实例,类爸爸的实例就试普通的类。

所以我们class 创建类的时候,其实是调用了类爸爸的函数type

In [335]: Demo_f = type(‘Demo‘,(),{})                                                                         

In [336]: Demo_f
Out[336]: __main__.Demo

In [337]: Demo_f.__class__
Out[337]: type

type如果用来实例化创建类的话,里面需要三个参数第一个类名,第二个是继承的父类,第三个是参数用字典的形式可以传入函数,也可以直接传入变量。

传入函数就好比class 里面的 def:后面的函数变量名,k的变量名,v的定义的函数。如果传入一个普通的值的话,就像普通的类属性,但话说class里面定义的函数也只不过是一个可以调用的属性而已。

我这里来写一个稍微复杂一点,既要继承父类属性的,又需要初始化的类。

def __init__(self, name):
    self.name = name

New_List = type(‘New_List‘, (list,), {‘__init__‘: __init__, ‘show‘: lambda self: self.name})

new_list = New_List(‘sidian‘)
print(new_list.show())
new_list.append(12)
print(new_list)
sidian
[12]

从type定义可以看到,该类继承了list的所有属性,然后有自定义了几个方法,一个初始化的方法,一个lamdba方法。

metaclass

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

metaclass,直译为元类,简单的解释就是:

当我们定义了类以后,就可以根据这个类创建出实例,所以:先定义类,然后创建实例。

后面我将复制廖雪峰大神的代码,逐行进行解释说明,这也对我的orm模型的创建有了更加深入的了解。

先上一个我自己写的最简单的例子:

class ModeMetaClass(type):
    def __new__(cls, *args, **kwargs):
        print(args, kwargs)        # 正常这个args就包含name, bases, attrs三个元素
        return type.__new__(cls, *args)

class Demo(list, metaclass=ModeMetaClass):

    def __init__(self, name, age):
        self.name = name
        self.age = age
        super(Demo, self).__init__()

    def show(self):
        return (f‘my name is {self.name},age is {self.age}‘)

demo = Demo(‘sidian‘, 66)
print(demo.show())
print(type(demo))
print(Demo.__mro__)
(‘Demo‘, (<class ‘list‘>,), {‘__module__‘: ‘__main__‘, ‘__qualname__‘: ‘Demo‘, ‘__init__‘: <function Demo.__init__ at 0x10afc4200>, ‘show‘: <function Demo.show at 0x10afc4290>, ‘__classcell__‘: <cell at 0x10afc6090: empty>}) {}
my name is sidian,age is 66
<class ‘__main__.Demo‘>
(<class ‘__main__.Demo‘>, <class ‘list‘>, <class ‘object‘>)

第一条输出其实在我没有实例化Demo就已经输出了,因为这是在创造类Demo的时候就需要输出了,所以它是最早执行的。

整个代码还是比较简单的,自定义了一个元类,但我除了输出,没有增加任何的功能。

但从ModeMetaClass输出可以看到Demo内部的说有属性都成为了ModeMetaClass元类attrs参数。

下面将上廖雪峰大神的代码:

# -*- coding: utf-8 -*-

class ModelMetaclass(type):
    # 这就是通过三个参数接收需要通过元类创建类的对象的属性
    def __new__(cls, name, bases, attrs):
        print(name,bases,attrs)
        if name==‘Model‘:        # 判断一下,如果是Model创建类,不经过修改使用原来的type创建
            return type.__new__(cls, name, bases, attrs)
        print(‘Found model: %s‘ % name)
        # 这里就不是Model类的情况下,创建类了
        mappings = dict()        # 定义一个字典
        for k, v in attrs.items():       # 将需要创建的类属性复制给k, v
            # 将v进行判断是不是Field的实例,因为需要创建的User里面4个字段的实例类的父类为Field
            if isinstance(v, Field):
                print(‘Found mapping: %s ==> %s‘ % (k, v))
                mappings[k] = v
        # 将本来attrs里面的参数中,删除value是Fiels实例的字段,
        # 假如不删除,通过__getattr__可能会由冲突。
        for k in mappings.keys():
            attrs.pop(k)
        # 将属性中带有实例方法的字段放入__mappings__中
        attrs[‘__mappings__‘] = mappings # 保存属性和列的映射关系
        attrs[‘__table__‘] = name # 假设表名和类名一致
        return type.__new__(cls, name, bases, attrs)

class Model(dict, metaclass=ModelMetaclass):
    # 基础父类dict初始化
    def __init__(self, **kw):
        super(Model, self).__init__(**kw)
    # 设置__getattr__,可以通过.来取出对象的属性值
    def __getattr__(self, key):
        try:
            return self[key]
        except KeyError:
            raise AttributeError(r"‘Model‘ object has no attribute ‘%s‘" % key)
    # 通过__setattr__,可以通过.取出来的值,然后直接赋值。
    def __setattr__(self, key, value):
        self[key] = value

    def save(self):
        fields = []
        params = []
        args = []
        for k, v in self.__mappings__.items():
            # fields列表中,添加User类属性中的实例的属性name
            fields.append(v.name)
            params.append(‘?‘)
            # 这个self,User调用刚好可以返回属性参数
            args.append(getattr(self, k, None))
        sql = ‘insert into %s (%s) values (%s)‘ % (self.__table__, ‘,‘.join(fields), ‘,‘.join(params))
        print(‘SQL: %s‘ % sql)
        print(‘ARGS: %s‘ % str(args))

class Field(object):

    def __init__(self, name, column_type):
        self.name = name
        self.column_type = column_type

    def __str__(self):
        return ‘<%s:%s>‘ % (self.__class__.__name__, self.name)

class StringField(Field):

    def __init__(self, name):
        super(StringField, self).__init__(name, ‘varchar(100)‘)

class IntegerField(Field):

    def __init__(self, name):
        super(IntegerField, self).__init__(name, ‘bigint‘)

class User(Model):
    # 定义类的属性到列的映射,这里的属性在使用元类创建是,将成为元类attrs的参数
    # 没个属性都是一个实例.
    id = IntegerField(‘id‘)
    name = StringField(‘username‘)
    email = StringField(‘email‘)
    password = StringField(‘password‘)

# 创建一个实例:
u = User(id=12345, name=‘Michael‘, email=‘[email protected]‘, password=‘my-pwd‘)
print(u.name)
# print(u.sex)
print(u.__mappings__)
print(u.__class__)
# 保存到数据库:
u.save()
Model (<class ‘dict‘>,) {‘__module__‘: ‘__main__‘, ‘__qualname__‘: ‘Model‘, ‘__init__‘: <function Model.__init__ at 0x11031d320>, ‘__getattr__‘: <function Model.__getattr__ at 0x11031d3b0>, ‘__setattr__‘: <function Model.__setattr__ at 0x11031d440>, ‘save‘: <function Model.save at 0x11031d4d0>, ‘__classcell__‘: <cell at 0x11031b490: empty>}
User (<class ‘__main__.Model‘>,) {‘__module__‘: ‘__main__‘, ‘__qualname__‘: ‘User‘, ‘id‘: <__main__.IntegerField object at 0x11031b7d0>, ‘name‘: <__main__.StringField object at 0x11031b810>, ‘email‘: <__main__.StringField object at 0x11031b850>, ‘password‘: <__main__.StringField object at 0x11031b890>}
Found model: User
Found mapping: id ==> <IntegerField:id>
Found mapping: name ==> <StringField:username>
Found mapping: email ==> <StringField:email>
Found mapping: password ==> <StringField:password>
Michael
{‘id‘: <__main__.IntegerField object at 0x11031b7d0>, ‘name‘: <__main__.StringField object at 0x11031b810>, ‘email‘: <__main__.StringField object at 0x11031b850>, ‘password‘: <__main__.StringField object at 0x11031b890>}
<class ‘__main__.User‘>
[‘__class__‘, ‘__delattr__‘, ‘__dict__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__init_subclass__‘, ‘__le__‘, ‘__lt__‘, ‘__module__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘__weakref__‘, ‘column_type‘, ‘name‘]
[‘__class__‘, ‘__delattr__‘, ‘__dict__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__init_subclass__‘, ‘__le__‘, ‘__lt__‘, ‘__module__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘__weakref__‘, ‘column_type‘, ‘name‘]
[‘__class__‘, ‘__delattr__‘, ‘__dict__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__init_subclass__‘, ‘__le__‘, ‘__lt__‘, ‘__module__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘__weakref__‘, ‘column_type‘, ‘name‘]
[‘__class__‘, ‘__delattr__‘, ‘__dict__‘, ‘__dir__‘, ‘__doc__‘, ‘__eq__‘, ‘__format__‘, ‘__ge__‘, ‘__getattribute__‘, ‘__gt__‘, ‘__hash__‘, ‘__init__‘, ‘__init_subclass__‘, ‘__le__‘, ‘__lt__‘, ‘__module__‘, ‘__ne__‘, ‘__new__‘, ‘__reduce__‘, ‘__reduce_ex__‘, ‘__repr__‘, ‘__setattr__‘, ‘__sizeof__‘, ‘__str__‘, ‘__subclasshook__‘, ‘__weakref__‘, ‘column_type‘, ‘name‘]
SQL: insert into User (id,username,email,password) values (?,?,?,?)
ARGS: [12345, ‘Michael‘, ‘[email protected]‘, ‘my-pwd‘]

整个分析来看,代码写的真漂亮,在User里面不定义任何方法,所有的方法都是从父类调用,父类的创建由元类创建,自身也可以调用父类的元类创建类。

这个写代码的风格还是我需要努力学习的,我梦想有生之年做一个架构师,觉得这些代码的学习对我的帮助还是非常大的。

原文地址:https://www.cnblogs.com/sidianok/p/11973701.html

时间: 2024-10-08 12:37:29

小白尝试写一篇类元编程记录。的相关文章

Python类元编程初探

在<流畅的Python>一书中提到: Classes are first-class object in Python, so a function can be used to create a new class ant any time, without using the class keyword. 在Python中,声明一个类可以有两种方法: >>> class X: ... a = 1 ... >>> X = type('X', (object

python元编程之使用动态属性实现定制类--特殊方法__setattr__,__getattribute__篇

问题:实现一个类,要求行为如同namedtuple:只存在给定名称的属性,不允许动态添加实例属性. 主要知识点在于: __setattr__,__getattr__,getattribute__,__delattr__特殊方法的实现使用. 代码如下: 1 """ 2 运行环境 3 python 3.7+ 4 """ 5 from collections OrderedDict, namedtuple 6 #以下为要包装的对象:1个命名元组,用于存

Java小白入门系列 第一篇 写在前面

2018年8月30日  22:00:17 郑州  多云 Sue Java小白入门系列 第一篇  写在前面 写在前面: 首先声明一下,本人也是正在学Java,并不是多么专业人士,只是最近受老师的启发,所以准备写个关于java新手入门系列的博客,包括搭建Java开发环境.Java入门知识,也会分享一些好用的软件及破解器之类的,一方面是巩固所学的知识,另一方面是给有兴趣的小白做练手.入门之用,本系列博客完全开放,所有资源不收任何费用,欢迎大家转发留言,入门之用,不喜勿喷,恶人绕道! Java是不是很难

Python-面向对象(三 元编程)

译注:这是一篇在Stack overflow上很热的帖子.提问者自称已经掌握了有关Python OOP编程中的各种概念,但始终觉得元类(metaclass)难以理解.他知道这肯定和自省有关,但仍然觉得不太明白,希望大家可以给出一些实际的例子和代码片段以帮助理解,以及在什么情况下需要进行元编程.于是e-satis同学给出了神一般的回复,该回复获得了985点的赞同点数,更有人评论说这段回复应该加入到Python的官方文档中去.而e-satis同学本人在Stack Overflow中的声望积分也高达6

C++模板元编程 - 3 逻辑结构,递归,一点列表的零碎,一点SFINAE

本来想把scanr,foldr什么的都写了的,一想太麻烦了,就算了,模板元编程差不多也该结束了,离开学还有10天,之前几天部门还要纳新什么的,写不了几天代码了,所以赶紧把这个结束掉,明天继续抄轮子叔的Win32库去. 逻辑结构和递归说白了就是做了一个If,一个For_N,If就和Excel里的If一样,For_N是把一个模板结构迭代N遍,为了所谓的方便,把If做成了宏,写起来还挺有意思的 1 template<typename TTest, typename TTrue, typename TF

使用 JavaScript 进行元编程(网站打不了)

Felix Woo 世界因我存在 首页 ThinkPage 新闻 天气 glickr 相册 留言板 友情链接 2007-8 8 使用 JavaScript 进行元编程 发表于: 16:50 | 分类: 开发技术 | 评论: 2 | 人气: 279 | 转自:http://benchwang.spaces.live.com/blog/cns!1621B5CAD6EB680B!149.entry Adam McCrea 写了篇使用 JavaScript 进行元编程的文章: Metaprogammin

ES6入门九:Symbol元编程

JS第七种数据类型:Symbol Symbol的应用场景 11个Symbol静态属性 Symbol元编程 一.JS第七种数据类型:Symbol 在ES6之前的JavaScript的基本数据类型有undefined.null.boolean.number.string.object,现在Symbol作为第七种基本数据类型.翻译symbol这个单词就是“符号,标志”的意思,顾名思义Symbol的应用场景也就离不开唯一性,想想“符号.标志”都是用来干嘛的?不就是用来标记特定事物的符号嘛,在程序中用来作

第14篇-JAVA GUI编程

第14篇-JAVA GUI编程 每篇一句 :道路一开始开辟的时候总是存在障碍的 初学心得: 原本下定决心才能开始的事情也变得理所当然 (笔者:JEEP/711)[JAVA笔记 | 时间:2017-04-25| JAVA GUI编程 ] 1.什么是界面 图形用户界面(Graphical User Interface,简称 GUI,又称图形用户接口)是指采用图形方式显示的计算机操作用户界面 与早期计算机使用的命令行界面相比,图形界面对于用户来说在视觉上更易于接受 2.Java 基础类 JFC 的基本

第15篇-JAVA JDBC编程

第15篇-JAVA JDBC编程 每篇一句 :对于勇敢者,尝试是一条崭新的生活之路 初学心得: 但对于坎坷与泥泞,能以平常之心视之,就非常不容易 (笔者:JEEP/711)[JAVA笔记 | 时间:2017-05-08| JAVA JDBC编程 ] 1.JDBC概述 通过使用JDBC API,Java程序可以非常方便地操作各种主流数据库,这是是Java语言的巨大魅力所在 由于Java语言的跨平台特性,所以使用JDBC API所编写的程序不仅可以实现跨数据库,还可以跨平台,具有非常优秀的可移植性