Python类元编程初探
Posted mrzysv5
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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,), dict(a=1))
可见类X
也是一个type类型的对象。
Import time和runtime
在import一个模块的时候,可以看出有哪些模块中的语句会执行。比如有模块a.py
:
A = 'A'
class ClassA:
print('print ClassA')
def printf():
print('print printf)
当我们执行import a
的时候:
>>> import a
print ClassA
>>> locals()
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <class '_frozen_importlib.BuiltinImporter'>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, 'a': <module 'a' from 'D:\\Workspace\\a.py'>}
可以看到,类ClassA
中的语句会被执行,可以根据根据代码所在在模块中的层级,将一些在顶级的代码称作top-level code。这类代码会在import time被执行。很显然,类中的代码块也会被执行。为什么模块a.py
中的函数printf
不会被执行呢?根据文章开头提到的,类也是一种对象。声明一个类,相当于执行type创建一个type对象。所以类声明语句会被执行,需要确定声明的类有哪些属性和方法。
__new__和__init__方法
官方文档给出的说明:
object.__new__(cls[, ...]), called to create a new instance of class cls.
object.__init__(self[, ...]), called after the instance has been created (by new()), but before it is returned to the caller.
new() is intended mainly to allow subclasses of immutable types (like int, str, or tuple) to customize instance creation. It is also commonly overridden in custom metaclasses in order to customize class creation.
在实例化一个类的对象的时候,首先会调用__new__
来确定会实例化哪个类,然后在调用__init__
来初始化具体的对象。一般很少使用这种特性。但是在编写一些框架的时候却很有用,比如编写ORM框架。
metaclass的具体使用
上文提到的,可以用type
来创建一个类。当type
不能满足需求的时候,就需要自定义type
方法了。
在廖雪峰的使用元类一文中,需要实现一个简单的ORM。比如定义了一个模型User
,它对应数据库中的users表,有四个字段,分别是id、name、email和password:
class User(Model):
# 定义类的属性到列的映射:
__tablename__ = 'users'
id = IntegerField('id')
name = StringField('username')
email = StringField('email')
password = StringField('password')
类属性(id、name、email和password)如何和表中的字段关联?如何只是声明一个这样的类,没有任何作用,解释器会调用type(‘User‘, (Model, ), {‘id‘: IntegerField(‘id‘)})
来创建type对象。很显然默认的type
方法不满足需要。所以需要自定义一个type
方法:
class ModelMetaclass(type):
def __new__(cls, name, bases, attrs):
if name=='Model':
return type.__new__(cls, name, bases, attrs)
mappings = dict()
for k, v in attrs.items():
if isinstance(v, Field):
print('Found mapping: %s==>%s' % (k, v))
mappings[k] = v
for k in mappings.keys():
attrs.pop(k)
print(cls)
attrs['__table__'] = attrs['__tablename__']
attrs['__mappings__'] = mappings # 保存属性和列的映射关系
print('ModelMetaclass.__new__')
return type.__new__(cls, name, bases, attrs)
这样就可以调用ModelMetaclass(‘User‘, (Model,), {‘id‘: IntegerField(‘id‘)})
来创建一个类对象。粘贴复制加魔改廖雪峰博文中的代码,省略部分代码:
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 ModelMetaclass(type):
def __new__(cls, name, bases, attrs):
if name=='Model':
return type.__new__(cls, name, bases, attrs)
mappings = dict()
for k, v in attrs.items():
if isinstance(v, Field):
print('Found mapping: %s==>%s' % (k, v))
mappings[k] = v
for k in mappings.keys():
attrs.pop(k)
print(cls)
attrs['__table__'] = name # 假设表名和类名一致
attrs['__mappings__'] = mappings # 保存属性和列的映射关系
print('ModelMetaclass.__new__')
print(attrs)
return type.__new__(cls, name, bases, attrs)
class Model(dict):
def save(self):
fields = []
params = []
args = []
for k, v in self.__mappings__.items():
fields.append(v.name)
params.append('?')
args.append(getattr(self, k, None))
sql = 'insert into %s (%s) values (%s)' % (self.__tablename__, ','.join(fields), ','.join(params))
print('SQL: %s' % sql)
print('ARGS: %s' % str(args))
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Model' object has no attribute '%s'" % key)
def __setattr__(self, key, value):
self[key] = value
User = ModelMetaclass('User', (Model, ), {'__tablename__': 'users', 'id': IntegerField('id')})
在上面的代码中,我们使用ModelMetaclass来动态的创建User
类,这和廖雪峰博文中在声明类的时候指明metaclass=ModelMetaclass
是一样的效果。sqlalchemy
中就是使用这种方式来动态的创建类的:
def declarative_base(
bind=None,
metadata=None,
mapper=None,
cls=object,
name="Base",
constructor=_declarative_constructor,
class_registry=None,
metaclass=DeclarativeMeta,
):
lcl_metadata = metadata or MetaData()
if bind:
lcl_metadata.bind = bind
if class_registry is None:
class_registry = weakref.WeakValueDictionary()
bases = not isinstance(cls, tuple) and (cls,) or cls
class_dict = dict(
_decl_class_registry=class_registry, metadata=lcl_metadata
)
if isinstance(cls, type):
class_dict["__doc__"] = cls.__doc__
if constructor:
class_dict["__init__"] = constructor
if mapper:
class_dict["__mapper_cls__"] = mapper
return metaclass(name, bases, class_dict)
以上是关于Python类元编程初探的主要内容,如果未能解决你的问题,请参考以下文章