# 元类应用典型案例
# 参考
# 元类应用典型案例
# Singleton单例模式
class SingletonMeta(type):
instance = None
def __call__(cls, *args, **kwargs):
if cls.instance is None:
cls.instance = super(SingletonMeta, cls).__call__(*args, **kwargs)
return cls.instance
class CurrentUser(object, metaclass=SingletonMeta):
def __init__(self, name=None):
super(CurrentUser, self).__init__()
self.name = name
def __str__(self):
return repr(self) + ":" + repr(self.name)
if __name__ == '__main__':
u = CurrentUser("liu")
print(u)
u2 = CurrentUser()
u2.name = "xin"
print(u2)
print(u)
assert u is u2
注:代码来自 https://github.com/Meteorix/python-design-patterns (opens new window) 。
这里重载的是type的call方法。改变了用户类实例化的行为,使其只能得到唯一的实例。
# 简易ORM框架
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().__init__(name, 'varchar(100)')
class IntegerField(Field):
def __init__(self, name):
super().__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):
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()
# 输出
"""
Found model: User
Found mapping: id ==> <IntegerField:id>
Found mapping: name ==> <StringField:username>
Found mapping: email ==> <StringField:email>
Found mapping: password ==> <StringField:password>
SQL: insert into User (id,username,email,password) values (?,?,?,?)
ARGS: [12345, 'Michael', 'test@orm.org', 'my-pwd']
"""
注:代码来自 https://www.liaoxuefeng.com/wiki/1016959663602400/1017592449371072 (opens new window)
这里重载的是type的new方法,因此改变的是用户类在定义时的行为。这里用户类是一个数据模型,元类编程做的是在用户定义一个数据模型类的时候,把所有是Field类型的属性收集起来,统一放到mapping属性中。这样基类的方法save可以直接通过mapping属性来保存需要保存的Field属性们。
# YAMLObject
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
# 使用
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)
yaml.load("""
--- !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(Monster(
name='Cave lizard', hp=[3,6], ac=16, attacks=['BITE','HURT']))
# 输出
"""
!Monster
ac: 16
attacks: [BITE, HURT]
hp: [3, 6]
name: Cave lizard
"""
这里重载的是type的init方法,因此改变的是用户类在定义时的行为。在定义了用户类Monster时,便通过cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml)将Monster类注册到系统。
# PyText
class ComponentMeta(type):
def __new__(metacls, typename, bases, namespace):
if "Config" not in namespace:
# We need to dynamically create a new Config class per
# instance rather than inheriting a single empty config class
# because components are registered uniquely by config class.
# If a parent class specifies a config class, inherit from it.
parent_config = next(
(base.Config for base in bases if hasattr(base, "Config")), None
)
if parent_config is not None:
class Config(parent_config):
pass
else:
class Config(ConfigBase):
pass
namespace["Config"] = Config
component_type = next(
(
base.__COMPONENT_TYPE__
for base in bases
if hasattr(base, "__COMPONENT_TYPE__")
),
namespace.get("__COMPONENT_TYPE__"),
)
new_cls = super().__new__(metacls, typename, bases, namespace)
new_cls.Config.__COMPONENT_TYPE__ = component_type
new_cls.Config.__name__ = f"{typename}.Config"
new_cls.Config.__COMPONENT__ = new_cls
new_cls.Config.__EXPANSIBLE__ = namespace.get("__EXPANSIBLE__")
if component_type:
Registry.add(component_type, new_cls, new_cls.Config)
return new_cls
这里重载的是type的new方法,因此改变的是用户类在定义时的行为。在定义了用户组件类时,便通过Registry.add(component_type, new_cls, new_cls.Config)将该组件类注册到系统。
← 定义有可选参数的元类 元类概述 →