https://www.cnblogs.com/ybjourney/p/14295872.html https://zhuanlan.zhihu.com/p/378294204 https://zhuanlan.zhihu.com/p/311160583 https://mp.weixin.qq.com/s/FsjFt7ZHG5AXhChghKn8qQ
对象是类的实例 类是type的实例 type() 接收三个参数:类的名称、父类(tuple形式传入)、绑定的方法或者属性
MetaClass
元类可以理解为是type的子类,继承type,重载call方法,目的是可以在创建类对象的时候作出一些修改 元类的作用过程:
- 拦截类的创建
- 拦截后,进行修改
- 修改完后,返回修改后的类
示例1:创建类时修改类的属性为大写
class TestMetaClass(type):
# def __init__(self, name, class_bases, class_dict):
# logger("this is metaclass __init__ func")
# logger(f"class name: {name}")
# logger(f"class parents as a tuple: {class_bases}")
# logger(f"包含类主体定义的命名空间并且会被复制到一个标准字典成为 __dict__ 属性:{class_dict}")
# super().__init__(name, class_bases, class_dict)
# # metaclass中__call__方法负责对象的创建,这就是为什么要重新定义的原因
# def __call__(cls, *args, **kwargs):
# logger("this is metaclass __call__ func")
# self = super().__call__(*args, **kwargs)
# return self
def __new__(cls, class_name, class_parents, class_attr):
logger("this meteclass __new__ func")
# 将类中非__开头的属性修改为大写
new_attr = {}
for name, value in class_attr.items():
logger(f"name={name}, value={value}")
if not name.startswith("__"):
new_attr[name.upper()] = value
logger(f"new_attr:{new_attr}")
# 调用超类的 __new__() 并返回
return super(TestMetaClass, cls).__new__(cls, class_name, class_parents, new_attr)
class B(metaclass=TestMetaClass):
def __init__(self, *args, **kwargs):
logger("this is class __init__ func")
super().__init__()
def __new__(cls, *args, **kwargs):
logger("this is class __new__ func")
self = super().__new__(cls)
return self
def func1(self):
logger("this is class instance func")
B()
B().FUNC1()
# [I 220520 18:03:01 debug:47] this meteclass __new__ func
# [I 220520 18:03:01 debug:50] name=__module__, value=__main__
# [I 220520 18:03:01 debug:50] name=__qualname__, value=B
# [I 220520 18:03:01 debug:50] name=__init__, value=<function B.__init__ at 0x7f6107b77ae8>
# [I 220520 18:03:01 debug:50] name=__new__, value=<function B.__new__ at 0x7f6107b77b70>
# [I 220520 18:03:01 debug:50] name=func1, value=<function B.func1 at 0x7f6107b77bf8>
# [I 220520 18:03:01 debug:50] name=__classcell__, value=<cell at 0x7f6108413198: empty>
# [I 220520 18:03:01 debug:53] new_attr:{'FUNC1': <function B.func1 at 0x7f6107b77bf8>}
# [I 220520 18:03:01 debug:72] this is class instance func
示例2: ORM框架设计
想要实现如下通过将关系数据库的一行映射为一个对象的操作,如下:
class Field(object):
"""定义Field类,它负责保存数据库表的字段名和字段类型"""
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):
super(IntegerField, self).__init__(name, "bigint")
class StringField(Field):
def __init__(self, name):
super(StringField, self).__init__(name, "varchar(100)")
class ModelMetaClass(type):
"""orm模型元类"""
def __new__(cls, name, bases, attrs):
if name == "Model":
return type.__new__(cls, name, bases, attrs)
logger(f"Found model: {name}")
logger(f"attrs:{attrs}")
mappings = {}
for k, v in attrs.items():
# 如果找到一个Field属性,就把它保存到一个__mappings__的dict中,同时从类属性中删除该Field属性(避免实例的属性遮盖类的同名属性)
if isinstance(v, Field):
logger(f"Found mappings : {k} ==> {v}")
mappings[k] = v
for k in mappings.keys():
attrs.pop(k)
attrs["__mappings__"] = mappings # 保存属性和列的映射关系
attrs.setdefault('__table__', name) # 当未定义__table__属性时,表名直接使用类名
return type.__new__(cls, name, bases, attrs)
class Model(dict, metaclass=ModelMetaClass):
"""Model 模型"""
def __init__(self, **kw):
"""一个基类如果有 __init__() 方法,则其所派生的类如果也有 __init__() 方法,
就必须显式地调用它以确保实例基类部分的正确初始化;例如: super().__init__([args...])."""
super(Model, self).__init__(**kw)
def __getattr__(self, key):
"""当对象的属性不存在时调用"""
try:
logger(f"{key}, call __getattr__ func")
return self[key]
except KeyError:
return AttributeError(f"'Model' object has no attribute {key}")
def __setattr__(self, key, value):
"""属性被赋值时调用"""
logger(f"{key}={value}, call __setattr__ func")
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 = f"insert into {self.__table__} ({','.join(fields)}) values ({','.join(params)})"
logger(f"sql={sql}")
logger(f"args={args}")
logger(f"params={params}")
class User(Model):
# 定义类的属性到列的映射
id = IntegerField("id")
name = StringField('username')
# 创建一个实例
user = User(id=123456, name="zaygee")
# 保存到数据库
user.save()
# [I 220520 18:57:05 debug:123] Found model: User
# [I 220520 18:57:05 debug:124] attrs:{'__module__': '__main__', '__qualname__': 'User', 'id': <__main__.IntegerField object at 0x7fb6fc19c7f0>, 'name': <__main__.StringField object at 0x7fb6fc19c828>}
# [I 220520 18:57:05 debug:130] Found mappings : id ==> <IntegerField: id>
# [I 220520 18:57:05 debug:130] Found mappings : name ==> <StringField: username>
# [I 220520 18:57:05 debug:151] id, call __getattr__ func
# [I 220520 18:57:05 debug:151] name, call __getattr__ func
# [I 220520 18:57:05 debug:172] sql=insert into User (id,username) values (?,?)
# [I 220520 18:57:05 debug:173] args=[123456, 'zaygee']
# [I 220520 18:57:05 debug:174] params=['?', '?']
使用init_subclass 替换元类
1、当所在类派生子类时此方法就会被调用。cls 将指向新的子类。如果定义为一个普通实例方法,此方法将被隐式地转换为类方法 2、 传入一个新类的关键字参数会被传给父类的 init_subclass。为了与其他使用 init_subclass 的类兼容,应当根据需要去掉部分关键字参数再将其余的传给基类 3、 这个方法隐式地被 @classmethod 装饰
class Validator:
"""校验器基类: 统一注册所有校验器类"""
_validators = {}
def __init_subclass__(cls, **kwargs):
print('{} 注册额外参数: {}'.format(cls.__name__, kwargs))
Validator._validators[cls.__name__] = cls
# 派生子类
class StringValidator(Validator, foo='bar'):
name = 'string'
# StringValidator 注册额外参数: {'foo': 'bar'}
# {'StringValidator': <class '__main__.StringValidator'>}
print(Validator._validators)
# {'StringValidator': <class '__main__.StringValidator'>}
print(StringValidator()._validators)
class Philosopher:
"""当有子类继承了 Philosopher ,那么 __init_subclass__ 就会调用"""
def __init_subclass__(cls, default_name, **kwargs):
print("call __init_subclass__ func, default_name is", default_name)
for k, v in kwargs.items():
type.__setattr__(cls, k, v)
class AustralianPhilosopher(Philosopher, default_name="Bruce", name="zaygee"):
pass
# call __init_subclass__ func, default_name is Bruce
# <class '__main__.Philosopher'>
print(Philosopher)
print(AustralianPhilosopher.name) # zaygee