Python 是一种强大的编程语言,一部分得益于其语言设计中独特的元类(Metaclass)机制。尽管元类的概念在刚开始接触时可能会让人感到困惑,但一旦理解了它们的工作原理和应用方式,我们就可以用它们做出强大且灵活的抽象。
在 Python 中,一切皆对象,包括类本身。类定义了对象的行为,而元类则定义了类的行为。简而言之,元类就是创建类的“类”。
符合Python对象的条件是:
从这里,大家就可以看出,Python中一切皆为对象,一切都符合这些条件
Python对象都会有三个特征:
class Job(object):
pass
j = Job()
print(j) #<__main__.Job object at 0x10f4f9750>
print(id(j)) #4553611344
print(j.__class__) #
print(Job) #
print(id(Job)) #140637422913424
print(Job.__class__) #
print(type(Job)) #
print(Job.__base__) #
print(Job.mro()) #[, ]
print(id(Job.__class__)) #4373786024
print(type(Job.__class__)) #
print(type(int)) #
print(type.__base__) #
从上面的结果我们看看出来:
可以看出来,我们以前学习的所有的类型都是type类型的实例(对象),或者说,都是由type类型创建的类对象,我们把type类型称为元类。
type、class、object的关系如下:
我们也可以自己构建元类,改变类的创建方式。
通过继承 type
类就可以自己定义元类,元类应该定义一个 __new__
方法。这个方法负责接收类定义的参数,并返回一个类对象。下面是一个简单的元类示例:
class MyMeta(type):
def __new__(cls, name, bases, dct):
x = super().__new__(cls, name, bases, dct)
x.attr = 100
return x
class MyClass(metaclass=MyMeta):
pass
print(MyClass.attr)
# 输出: 100
print(type(int)) #
print(type(MyClass)) #
print(MyClass.__base__) #
上面的例子可以看到:
attr,
使用 MyMeta
作为元类,定义了一个类 MyClass,MyClass
确实拥有了 attr
属性。正常的类都是由type创建出来的,但我们也可以创建自己的元类,让他继承自type,并设定某个类使用它.而这个类的子类也就同样是这个元类产生出的类.一个基本的元类如下:
class MetaClass(type):
def __new__(cls,name,bases,namespace):
#执行操作…
#早于__init__方法执行,必须返回空对象,由于该方法是调用类后第一个运行的方法,此时并没有对象产生,因此该方法的第一个参数必须是类本身(MyMeta),*args, **kwargs用来接收调用元类产生对象所需的参数(类名 类的基类 名称空间)
#其实下面那行也可以改成return type(name,bases,namespace),但这样保持了代码的一致性.
return super().__new__(cls,name,bases,namespace)
@classmethod
def __prepare__(cls,name,bases,**kwargs):
return super().__prepare__(name,bases,**kwargs)
#通过__init__控制类的产生,调用自定义元类与调用内置元类type的方式相同,需要传入类名、类的父类们、类体代码的名称空间,__init__方法中第一个参数来自于__new__方法产生的空对象。
def __init__(self,name,bases,namespace,**kwargs):
#执行操作,注意这里虽然是self,但其实就是指我们的类,因为这个类的实例是类
return super().__init__(name,bases,namespace,**kwargs)
def __call__(self,*args,**kwargs):
return super().__call__(*args,**kwargs)
只要调用类,就会依次调用类中的__new__
方法和__init__
方法;__new__
方法返回一个空对象,就类似一张白纸;__init__
获取__new__
方法中产生的白纸
在上面画不同的图案。
除通过直接定义外,也可以通过type()来定义一个新类型,语法为:
type(clsname, bases, dict, **kwds) -> a new type
其中:
def get(self) -> None:
"""定义类内部需要运行的函数"""
print("类获取了一个信息", self.info)
MyClass = type("MyClass", (object, ), {"get": get, "info": "hello"})
c = MyClass()
if hasattr(c, "get"): # 判断是否有get函数
getattr(c, "get", None)() # 获取get函数,并运行函数
else:
print("该类内部没有get函数")
实际上,解释器在解析类的声明后,也是采用type的方式创建的类对象。
使用type创建新类型往往应用在要创建许多类型的创建中,比如我们再ORM场景中,需要将许多表映射为对象类型,这个时候就可以使用该功能,自动化的创建所有表对象类型,并且能为这些表对象类型提供公共方法。
使用函数也可以作为元类,实现一个将类里面的所有属性名称转换为大写:
# 元类会自动获取通常传给`type`的参数
def upper_attr(_class, _object, _attr):
"""
返回一个类对象,将其属性置为大写
"""
# 过滤出所有开头不为'__'的属性,置为大写
uppercase_attr = {}
for name, val in _attr.items():
if not name.startswith('__'):
uppercase_attr[name.upper()] = val
else:
uppercase_attr[name] = val
# 利用'type'创建类,同时将这个类进行返回
return type(_class, _object, uppercase_attr)
class Foo(metaclass=upper_attr): # 创建对象时,其会经过 metaclass 来创建,再使用自身的方法进行实例化
bar = 'bip'
print(hasattr(Foo, 'bar'))
print(hasattr(Foo, 'BAR'))
f = Foo()
print(f.BAR)
类做元类:
class Meta(type):
def __new__(cls, name, bases, attrs):
print("执行了元类 Meta!")
attrs['author'] = "xiaoyang-sir"
spam_class = super().__new__(cls, name, bases, attrs)
print(spam_class)
return spam_class
class Spam(metaclass=Meta):
def __init__(self, website):
self.website = website
print(Spam.author)
"""
Out:
执行了元类 Meta!
xiaoyang-sir
"""
尽管元类是非常强大的工具,但它也是非常复杂的工具,所以应该在需要的时候才使用。以下是一些元类的典型应用场景:
所谓单例,就是这个类型的实例对象有且只能有一个,不能使用对象声明或其他方式创建该类的多个实例,可以通过元类实现单例:
class SingleMeta(type):
def __init__(self, *args, **kwargs):
print('SingleMeta.__init__ ')
super().__init__(*args, **kwargs)
self.instance = None
def __new__(cls, *args, **kwargs):
print('SingleMeta.__new__')
new_obj = super().__new__(cls, *args, **kwargs)
return new_obj
def __call__(self, *args, **kwargs):
print('SingleMeta.__call__ ')
if not self.instance:
self.instance = self.__new__(self)
self.__init__(self.instance, *args, **kwargs)
return self.instance
class Foo(metaclass=SingleMeta):
def __init__(self, *args, **kwargs):
print('Foo.__init__ ')
self.__name = kwargs['name']
def __new__(cls, *args, **kwargs):
print('Foo.__new__ ')
return super().__new__(cls)
@property
def name(self):
return self.__name
@name.setter
def name(self, value):
self.__name = value
foo1 = Foo(name='python')
foo2 = Foo(name='java')
print(id(foo1))
print(id(foo2))
‘’'
SingleMeta.__new__
SingleMeta.__init__
SingleMeta.__call__
Foo.__new__
Foo.__init__
SingleMeta.__call__
Foo.__init__
4391594832
4391594832
‘''
meta.__call__()
方法的调用是在实例对象__new__()、__init__()
之前执行。
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 IntegerField(Field):
def __init__(self, name, column_type='int(11)'):
super(IntegerField, self).__init__(name, column_type)
class StringField(Field):
def __init__(self, name, column_type='varchar(100)'):
super(StringField, self).__init__(name, column_type)
class ModelMetaClass(type):
def __new__(cls, class_name, class_parents, class_attr):
if class_name == 'Model':
return type.__new__(cls, class_name, class_parents, class_attr)
print('found model %s' % class_name)
mappings = {}
for name, value in class_attr.items():
if isinstance(value, Field):
mappings[name] = value
for k in mappings.keys():
class_attr.pop(k)
class_attr['__mappings__'] = mappings
class_attr['__table__'] = class_name.lower()
return type.__new__(cls, class_name, class_parents, class_attr)
class Model(dict, metaclass=ModelMetaClass):
def __init__(self, **kwargs):
super(Model, self).__init__(**kwargs)
def __getattr__(self, key):
try:
return str(self[key])
except KeyError:
raise AttributeError(r"'Model' object has no attribute '%s'" % key)
def __setattr__(self, key, value):
self[key] = str(value)
def save(self):
fields = []
args = []
for k, v in self.__mappings__.items():
print(k, v, v.name, v.column_type)
fields.append(v.name)
args.append(getattr(self, k))
sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(args))
print('SQL: %s' % sql)
print('ARGS: %s' % str(args))
class User(Model):
id = IntegerField('id')
name = StringField('name')
age = IntegerField('age')
address = StringField('address')
user = User(id='1', name='daocoder', age=27, address='anhui')
user.save()
User类定义继承自父类Model,且有4个属性,4个属性分别继承自IntegerField和StringField,这两个继承自Field,这个不谈。聚焦Model。
1、实例化User时,去找父类Model,发现父类拥有metaclass属性值为ModelMetaClass,即它是由一个自定义的元类来创建的类,向上寻找ModelMetaClass,这个类是继承自type。需要先创建它的实例对象。调用其静态方法new,这里面4个参数(cls, class_name, class_parents, class_attr),分别为ModelMetaClass的类对象、Model类名、父类(dict, )元组、自身内置属性。类名为Model时,直接创建type.__new__(cls, class_name, class_parents, class_attr)
并返回。再调用Model类的init方法,调用了父类dict的init的方法。父类Model作为类对象创建完成。
2、开始User类对象的创建,Model已有,然后开始创建User,还是向上找到了ModelMetaClass,这时的4个参数分别是(cls, class_name, class_parents, class_attr),分别为ModelMetaClass的类对象、User类名、父类(Model, )元组、自身内置属性包含id,name,age,address等。然后判断类名不是Model,继续向下,将User属性遍历,其实例自Field的属性封装为User类对象的mappings属性,类名User为User类对象的table属性。