当前位置:   article > 正文

创建类python代码_Python使用元类创建类

创建类python代码_Python使用元类创建类

Python的type函数能够返回对象的类型,先看下面一段代码

class Model(object):

pass

a = Model()

print(type(a))

#

输出的结果是a的类型:“Model”,这个是在意料之中的

不过,Python中的类也是一个对象,来看一下它的类型:

print(type(Model))

#

Model返回的类型是Type;可以这样理解,因为Model是类,也就是一种类型(type),所以创建一个类就是是创建了一种类型(即type的实例)

上面说的有点绕,简单了说就是:创建一个类就是创建了一个type的实例

所以很自然的一个疑问是,如果类是type的一个对象,那么这个类是如何与type关联起来的?

在定义类的时候,可以给它指定一个元类(metaclass)

class Model(object, metaclass=type):

pass

print(type(Model))

#

这里把Model的metaclass设置成了type,输出类型仍是‘type’,似乎没什么作用,接着:

class ModelMeta(type):

pass

class Model(object, metaclass=ModelMeta):

pass

print(type(Model))

#

现在先定义一个ModelMeta类,然后将Model的元类设置成ModelMeta,再打印Model的类型,结果变成了‘ModelMeta’类型

到这里可以初步认为,类作为一个对象,是由它的元类所创建,而默认的元类就是type,下面来进一步确认

class ModelMeta(type):

def __init__(self, *args, **kwargs):

super().__init__(*args, **kwargs)

print("init a", self)

print("init a", self.__class__.__name__)

class Model(object, metaclass=ModelMeta):

pass

# init a

# init a ModelMeta

上面的代码除了定义类Model和它的元类ModelMeta,其他任何代码都没有,但是还是打印了两句话:init a

init a ModelMeta

对照代码可以知道这是ModelMeta类的__init__执行的结果,也就是说有一个ModelMeta实例被创建了

因为在程序运行之后,python解释器会将所有的类、函数等都创建好,所以上面通过追踪ModelMeta类的init过程,证明了Model类确实是由它的metaclass,也就是ModelMeta这个类所创建的对象

在搞清楚这个原理后,就可以干很多事情了,例如,在原来的代码上,加上句打印:

m = Model()

print(m)

# <__main__.model object at>

上面创建一个Model对象,打印一切正常,再看看下面的代码:

class ModelMeta(type):

def __call__(self, *args, **kwargs):

print("hello")

class Model(object, metaclass=ModelMeta):

pass

m = Model()

print(m)

# hello

# None

可以看到,这一次没有创建成功Model对象,而是返回了一个None

关键在于ModelMeta的魔法方法__call__,因为Model是ModelMeta的一个对象,所以执行

Model()

实际上是就执行了ModelMeta的__call__方法,而该方法里面并没有执行创建对象的步骤,只是打印了一句hello,所以就返回了一个None

现在再修改一下:

def __call__(self, *args, **kwargs):

print("hello")

return super().__call__(*args, **kwargs)

...

m = Model()

print(m)

# hello

# <__main__.model object at>

这一次正常创建了Model对象,所以,当有时候有某种需要不希望一个类能够创建对象(比如只希望这个类提供一系列静态方法)时,可以使用这种方法:

class ModelMeta(type):

def __call__(self, *args, **kwargs):

raise TypeError("NO")

class Model(object, metaclass=ModelMeta):

@staticmethod

def func():

print("fun")

再比如说单例模式的实现:

class Singleton(type):

def __init__(self, *args, **kwargs):

self.__instance = None

super().__init__(*args, **kwargs)

def __call__(self, *args, **kwargs):

if self.__instance is None:

self.__instance = super().__call__(*args, **kwargs)

return self.__instance

else:

return self.__instance

class Model(metaclass=Singleton):

pass

a = Model()

print(a)

b = Model()

print(b)

# <__main__.model object at>

# <__main__.model object at>

可以看到a和b是同一个对象,还有一些设计模式,例如工厂模式等等也可以通过元类轻松实现

然后是通过重载元类的__new__方法,来对类进行动态改造:

class ModelMetaclass(type):

def __new__(cls, name, bases, attrs):

print('name:', name)

print('bases:', bases)

for k, v in attrs.items(): print('{}:{}'.format(k, v))

class Model(object, metaclass=ModelMetaclass):

class_attribute_1 = 1

class_attribute_2 = 'a'

def __init__(self):

self.obj_attribute_1 = 5

def func(self):

pass

# name: Model

# bases: (,)

# __module__: __main__

# __qualname__: Model

# class_attribute_1: 1

# class_attribute_2: a

# __init__:

# func:

在创建Model类的时候,因为它的元类是ModelMetaclass,所以会创建一个ModelMetaclass的实例,自然而然的会调用到它的__new__方法,从输出中可以看到,参数name是要创建的类的名称,bases是这个类的基类,而attrs是个字典,包括所有这个类的属性(是类的属性,不是对象的属性)

这里的参数和内置的type函数的参数是一致的,如果使用type函数动态创建类型的话也是使用这三个参数:

new_type = type('Hello', (object,), {'say': lambda self: print('hello')})

print(type(new_type))

t = new_type()

t.say()

#

# hello

上面,使用type动态创建了一个类型(也就是类),并且给了它一个say方法;可以看到,成功创建了一个类,并且调用了这个方法

铺垫都讨论完了,最后贴一段抄来的代码做纪念:

class Field(object):

def __init__(self, name, column_type):

self.name = name

self.column_type = column_type

def __str__(self):

return '' % (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)

print('Found model:%s' % name)

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)

attrs['__mappings__'] = mappings # 保存属性和列的映射关系

attrs['__table__'] = name # 假设表名和类名一致

return type.__new__(cls, name, bases, attrs)

class Model(dict, metaclass=ModelMetaclass):

def __init__(self, **kw):

super(Model, self).__init__(**kw)

def __getattr__(self, key):

'''重载__getattr__, __setattr__方法使子类可以像正常的类使用'''

try:

return self[key]

except KeyError:

raise AttributeError(r"'Model' object has no attribute '%s'" % key)

def __setattr__(self, key, value):

self[key] = value

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.__table__, ','.join(fields), ','.join(params))

print('SQL:%s' % sql)

print('ARGS:%s' % str(args))

class User(Model):

# 定义类的属性到列的映射:

id = IntegerField('id')

name = StringField('username')

email = StringField('email')

password = StringField('password')

# 创建一个实例:

u = User(id=12345, name='Michael', email='test@orm.org', password='my-pwd')

# # 保存到数据库:

u.save()

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/空白诗007/article/detail/929533
推荐阅读
相关标签
  

闽ICP备14008679号