《在Python中用法元类的教程_》由会员分享,可在线阅读,更多相关《在Python中用法元类的教程_(13页珍藏版)》请在金锄头文库上搜索。
1、在Python中用法元类的教程_ 这篇文章主要介绍了在Python中用法元类的教程,是Python当中的基础学问,代码基于Python2.x版本,需要的伴侣可以参考下 type() 动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的。 比方说我们要定义一个Hello的class,就写一个hello.py模块: class Hello(object): def hello(self, name=world): print(Hello, %s. % name) 当Python说明器载入hello模块时,就会依次执行该模块的全部语句,执行结果就是动态创建出一个H
2、ello的class对象,测试如下: from hello import Hello h = Hello() h.hello() Hello, world. print(type(Hello) type type print(type(h) class hello.Hello type()函数可以查看一个类型或变量的类型,Hello是一个class,它的类型就是type,而h是一个实例,它的类型就是class Hello。 我们说class的定义是运行时动态创建的,而创建class的方法就是用法type()函数。 type()函数既可以返回一个对象的类型,又可以创建出新的类型,比如,我们可以通
3、过type()函数创建出Hello类,而无需通过class Hello(object).的定义: def fn(self, name=world): # 先定义函数 . print(Hello, %s. % name) . Hello = type(Hello, (object,), dict(hello=fn) # 创建Hello class h = Hello() h.hello() Hello, world. print(type(Hello) type type print(type(h) class _main_.Hello 要创建一个class对象,type()函数依次传入3个参数
4、: class的名称; 继承的父类集合,留意Python支持多重继承,假如只有一个父类,别忘了tuple的单元素写法; class的方法名称与函数绑定,这里我们把函数fn绑定到方法名hello上。 通过type()函数创建的类和挺直写class是完全一样的,由于Python说明器遇到class定义时,仅仅是扫描一下class定义的语法,然后调用type()函数创建出class。 正常状况下,我们都用class Xxx.来定义类,但是,type()函数也允许我们动态创建出类来,也就是说,动态语言本身支持运行期动态创建类,这和静态语言有特别大的不同,要在静态语言运行期创建类,必需构造源代码字符串再
5、调用编译器,或者借助一些工具生成字节码实现,本质上都是动态编译,会特别简单。 metaclass 除了用法type()动态创建类以外,要掌握类的创建行为,还可以用法metaclass。 metaclass,直译为元类,简洁的说明就是: 当我们定义了类以后,就可以依据这个类创建出实例,所以:先定义类,然后创建实例。 但是假如我们想创建出类呢?那就必需依据metaclass创建出类,所以:先定义metaclass,然后创建类。 连接起来就是:先定义metaclass,就可以创建类,最终创建实例。 所以,metaclass允许你创建类或者修改类。换句话说,你可以把类看成是metaclass创建出来的
6、“实例”。 metaclass是Python面对对象里最难理解,也是最难用法的魔术代码。正常状况下,你不会碰到需要用法metaclass的状况,所以,以下内容看不懂也没关系,由于基本上你不会用到。 我们先看一个简洁的例子,这个metaclass可以给我们自定义的MyList增加一个add方法: 定义ListMetaclass,根据默认习惯,metaclass的类名总是以Metaclass结尾,以便清晰地表示这是一个metaclass: # metaclass是创建类,所以必需从type类型派生: class ListMetaclass(type): def _new_(cls, name, b
7、ases, attrs): attrsadd = lambda self, value: self.append(value) return type._new_(cls, name, bases, attrs) class MyList(list): _metaclass_ = ListMetaclass # 指示用法ListMetaclass来定制类 当我们写下_metaclass_ = ListMetaclass语句时,魔术就生效了,它指示Python说明器在创建MyList时,要通过ListMetaclass._new_()来创建,在此,我们可以修改类的定义,比如,加上新的方法,然后,
8、返回修改后的定义。 _new_()方法接收到的参数依次是: 当前预备创建的类的对象; 类的名字; 类继承的父类集合; 类的方法集合。 测试一下MyList是否可以调用add()方法: L = MyList() L.add(1) L 1 而一般的list没有add()方法: l = list() l.add(1) Traceback (most recent call last): File stdin, line 1, in module AttributeError: list object has no attribute add 动态修改有什么意义?挺直在MyList定义中写上add()
9、方法不是更简洁吗?正常状况下,的确应当挺直写,通过metaclass修改纯属变态。 但是,总会遇到需要通过metaclass修改类定义的。ORM就是一个典型的例子。 ORM全称“Object Relational Mapping”,即对象-关系映射,就是把关系数据库的一行映射为一个对象,也就是一个类对应一个表,这样,写代码更简洁,不用挺直操作SQL语句。 要编写一个ORM框架,全部的类都只能动态定义,由于只有用法者才能依据表的结构定义出对应的类来。 让我们来尝试编写一个ORM框架。 编写底层模块的第一步,就是先把调用接口写出来。比如,用法者假如用法这个ORM框架,想定义一个User类来操作对应
10、的数据库表User,我们期盼他写出这样的代码: class User(Model): # 定义类的属性到列的映射: id = IntegerField(id) name = StringField(username) email = StringField(email) password = StringField(password) # 创建一个实例: u = User(id=12345, name=Michael, password=my-pwd) # 保存到数据库: u.save() 其中,父类Model和属性类型StringField、IntegerField是由ORM框架供应的,剩下
11、的魔术方法比如save()全部由metaclass自动完成。虽然metaclass的编写会比较简单,但ORM的用法者用起来却特别简洁。 现在,我们就按上面的接口来实现该ORM。 首先来定义Field类,它负责保存数据库表的字段名和字段类型: 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) 在Field的基
12、础上,进一步定义各种类型的Field,比如StringField,IntegerField等等: 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) 下一步,就是编写最简单的ModelMetaclass了: class ModelMetaclass(ty
13、pe): def _new_(cls, name, bases, attrs): if name=Model: return type._new_(cls, name, bases, attrs) mappings = dict() for k, v in attrs.iteritems(): if isinstance(v, Field): print(Found mapping: %s=%s % (k, v) mappingsk = v for k in mappings.iterkeys(): attrs.pop(k) attrs_table_ = name # 假设表名和类名全都 at
14、trs_mappings_ = mappings # 保存属性和列的映射关系 return type._new_(cls, name, bases, attrs) 以及基类Model: class Model(dict): _metaclass_ = ModelMetaclass def _init_(self, *kw): super(Model, self)._init_(*kw) def _getattr_(self, key): try: return selfkey except KeyError: raise AttributeError(rModel object has no attribute %s % key) def _setattr_(self, key, value): selfkey = value def save(self): fields = params =