python的元类
1.类是如何产生的
Python扫描到class的语法的时候,就会调用type函数进行类的创建
2.如何使用type创建类
# 准备一个基类(父类)
class BaseClass:
def talk(self):
print("i am people")
# 准备一个方法
def say(self):
print("hello")
# 使用type来创建User类
User = type("User", (BaseClass, ), {"name":"user", "say":say})
用户自定义类,只不过是type类的__call__
运算符重载
class MyClass:
data = 1
instance = MyClass()
print(MyClass, instance) # 输出 (__main__.MyClass, < __main__.MyClass instance at 0x7fe4f0b00ab8 >)
print(instance.data) # 输出:1
MyClass = type('MyClass', (), {'data': 1})
instance = MyClass()
print(MyClass, instance) # 输出 (__main__.MyClass, < __main__.MyClass at 0x7fe4f0aea5d0 >)
print(instance.data) # 输出:1
metaclass是type 的子类,通过替换type的__call__
运算符重载机制,“超越变形”正常的类
class = type(classname, superclasses, attributedict)
# 变为了
class = MyMeta(classname, superclasses, attributedict)
3. 理解什么是元类
-
元类(metaclass)就是创建类的模板
-
type是Python在背后用来创建所有类的元类 连type自己也是由type自己创建的
-
metaclass: 超越变形特性 , 类是元类的实例,所以在创建一个普通类时,其实会走元类的
__new__
, 对普通类进行实例化时,实际是对一个元类的实例(也就是普通类)进行直接调用,所以会走进元类的__call__
class MetaSingleton(type): def __call__(cls, *args, **kwargs): print("cls:{}".format(cls.__name__)) print("====1====") if not hasattr(cls, "_instance"): print("====2====") cls._instance = type.__call__(cls, *args, **kwargs) return cls._instance class User(metaclass=MetaSingleton): def __init__(self, *args, **kw): print("====3====") for k, v in kw: setattr(self, k, v) user = User("nb")
4.使用元类的意义
对类进行定制修改, 使用元类来动态生成元类的实例, 元类的作用就是创建API
,一个最典型的应用是 Django ORM
import numbers
class Field:
pass
class IntField(Field):
def __init__(self, name):
self.name = name
self._value = None
def __get__(self, instance, owner):
return self._value
def __set__(self, instance, value):
if not isinstance(value, numbers.Integral):
raise ValueError("int value need")
self._value = value
class StrField(Field):
def __init__(self, name):
self.name = name
self._value = None
def __get__(self, instance, owner):
return self._value
def __set__(self, instance, value):
if not isinstance(value, str):
raise ValueError("string value need")
self._value = value
class ModelMetaClass(type):
def __new__(cls, name, bases, attrs):
print(name)
if name == "BaseModel":
# 第一次进入__new__是创建BaseModel类,name="BaseModel"
# 第二次进入__new__是创建User类及其实例,name="User"
return super().__new__(cls, name, bases, attrs)
# 根据属性类型,取出字段
fields = {k: v for k, v in attrs.items() if isinstance(v, Field)}
# 如果User中有指定Meta信息,比如表名,就以此为准
# 如果没有指定,就默认以 类名的小写 做为表名,比如User类,表名就是user
_meta = attrs.get("Meta", None)
db_table = name.lower()
if _meta is not None:
table = getattr(_meta, "db_table", None)
if table is not None:
db_table = table
# 注意原来由User传递过来的各项参数attrs,最好原模原样的返回,
# 如果不返回,有可能下面的数据描述符不起作用
# 除此之外,我们可以往里面添加我们自定义的参数
attrs["db_table"] = db_table
attrs["fields"] = fields
return super().__new__(cls, name, bases, attrs)
class BaseModel(metaclass=ModelMetaClass):
def __init__(self, *args, **kw):
for k, v in kw.items():
# 这里执行赋值操作,会进行数据描述符的__set__逻辑
setattr(self, k, v)
return super().__init__()
def save(self):
db_columns = []
db_values = []
for column, value in self.fields.items():
db_columns.append(str(column))
db_values.append(str(getattr(self, column)))
sql = "insert into {table} ({columns}) values({values})".format(
table=self.db_table, columns=','.join(db_columns),
values=','.join(db_values))
print(sql)
pass
class User(BaseModel):
id = IntField('id')
name = StrField('username')
email = StrField('email')
password = StrField('password')
class Meta:
db_table = "user"
if __name__ == '__main__':
# 实例化成一条记录
u = User(id=20230327, name="nb", email="nb@163.com", password="abc123")
# 保存这条记录
u.save()
综上,元类的__new__
和普通类的不一样:
- 元类的
__new__
在创建类时就会进入,它可以获取到上层类的一切属性和方法,包括类名,魔法方法。 - 而普通类的
__new__
在实例化时就会进入,它仅能获取到实例化时外界传入的属性
所有的Python的用户定义类,都是 type 这个类的实例
import yaml
from yaml import Loader
registry = {}
def add_constructor(target_class):
registry[target_class.yaml_tag] = target_class
class Monster(yaml.YAMLObject):
yaml_tag = u'!Monster'
def __init__(self, name, hp, ac, attacks):
self.name = name
self.hp = hp
self.ac = ac
self.attacks = attacks
def __repr__(self):
return "%s(name=%r, hp=%r, ac=%r, attacks=%r)" % (
self.__class__.__name__, self.name, self.hp, self.ac,
self.attacks)
class YAMLObjectMetaclass(type):
def __init__(cls, name, bases, kwds):
super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds)
if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None:
cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml)
# 省略其余定义
class YAMLObject(metaclass=YAMLObjectMetaclass):
yaml_loader = Loader
yaml_tag = ''
from_yaml = ''
class Monster2(YAMLObject):
yaml_tag = u'!Monster'
def __init__(self, name, hp, ac, attacks):
self.name = name
self.hp = hp
self.ac = ac
self.attacks = attacks
def __repr__(self):
return "%s(name=%r, hp=%r, ac=%r, attacks=%r)" % (
self.__class__.__name__, self.name, self.hp, self.ac,
self.attacks)
if __name__ == '__main__':
# add_constructor(Monster)
#
# yaml.load_all("""
# --- !Monster
# name: Cave spider
# hp: [2,6] # 2d6
# ac: 16
# attacks: [BITE, HURT]
# """)
#
# Monster(name='Cave spider', hp=[2, 6], ac=16, attacks=['BITE', 'HURT'])
print(yaml.dump(Monster2(name='Cave lizard', hp=[3, 6], ac=16, attacks=['BITE', 'HURT'])))