定义: 创建对象的对象(object是所有类的超类,而且type也是继承自object;所有对象创建自type,而且object也是type的实例。)
作用: 可以在程序运行的过程中,使用代码动态的去定义一个类(区别于那种传统的先用class定义完你用的类,然后实例化使用的方式)
适用场景:
There are numberours use cases for metaclasses. Just to name a few:
* logging and profiling 日志和解析
* interface checking 接口检查
* registering classes at creation time
* automatically adding new methods 自动添加方法
* automatically propery creation 自动创建属性
* proxies 代理
* automatic resource locking/synchronization 自动资源锁和同步
metaclass主要的使用情况就是用来创建API,使用metaclass的一个典型的例子是Django ORM。
Django使得很多复杂的逻辑仅暴露一个简单的API接口就可以调用,这正是通过metaclass实现的。metaclass会根据需要重新实现这些复杂操作所需要的真正的代码。
很多可以直接通过装饰器完成
use:
type(class_name, base_class, attrs)
e.g1:
https://blog.csdn.net/lanix516/article/details/89326849
NewClass = type("NewCls", (), {"name":"bob"})
a = NewClass()
a.name
# py2:
class NewClass:
__metaclass__ = MyMetaClass
# py3:
class NewClass(metaclass=MyMetaClass):
pass
class Action(object):
def __init__(self, act_desc):
self.desc = act_desc
class ManMeta(type):
def __new__(cls, name, bases, attrs):
actives = {}
print attrs
# print name
# print bases
# print "*" * 50
for k, v in attrs.items():
print k, v
if isinstance(v, Action):
cls.add_action(actives, k, v)
attrs.update(actives)
print actives
print attrs
return type.__new__(cls, name, bases, attrs)
@classmethod
def add_action(cls, action_dict, active_name, action_obj):
def active(self):
self.state = active_name
print action_obj.desc
action_dict[active_name] = active # active 是一个个地址,函数的引用
class Hero(object):
__metaclass__ = ManMeta
walk = Action("from on place to another")
fight = Action("use weapon to fight")
die = Action("your hero is dying now")
sleep = Action("fall asleep")
def __init__(self):
self.state = ""
z = Hero()
z.fight() # use weapon to fight
print z.state # 'fight'
e.g.2:
https://www.cnblogs.com/yjt1993/p/11103368.html
#定义ItemMetaClass,继承type
# metaclass 类的 __new__ 方法的作用是:当程序使用 class 定义新类时,如果指定了 metaclass,那么 metaclass 的 __new__ 方法就会被自动执行。
class ItemMetaClass(type):
# cls 代表动态修改的类
# name 代表动态修改的类名
# bases 代表被动态修改的类的所有父类
# attrs 代表被动态修改的类的所有属性、方法组成的字典
def __new__(cls, name, bases, attrs):
#动态为该类添加一个cal_price方法
attrs['cal_price'] = lambda self:self.price * self._discount
return type.__new__(cls, name, bases, attrs)
#定义book类
class Book(metaclass=ItemMetaClass):
__slots__ = ('name', 'price', '_discount')
def __init__(self, name, price):
self.name = name
self.price = price
@property
def discount(self):
return self._discount
@discount.setter
def discount(self, discount):
self._discount = discount
#定义cellPhone类
class CellPhone(metaclass=ItemMetaClass):
__slots__ = ('price', '_discount')
def __init__(self, price):
self.price = price
@property
def discount(self):
return self._discount
@discount.setter
def discount(self, discount):
self._discount = discount
#Book类实例化
b = Book('Python基础教程', 89)
b.discount = 0.8
#Book类的cal_price()方法
print(b.cal_price())
#CellPhone类实例化
cp = CellPhone(2300)
cp.discount = 0.85
#CellPhone类的cel_price方法
print(cp.cal_price())
# result
71.2
1955.0
e.g.3:
https://blog.csdn.net/jiguanglong/article/details/93204314
实现orm
动态加载(
现在你有 1 万个不同格式的 YAML 配置文件,本来你需要写 1 万个类来加载这些配置文件,有了 metaclass,你只需要实现一个 metaclass 超类,然后再实现一个子类继承这个 metaclass,就可以根据不同的配置文件自动拉取不同的类,这极大地提高了效率。
)
import yaml
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
#yaml源码
# Python 2/3 相同部分
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)
# Python 3
class YAMLObject(metaclass=YAMLObjectMetaclass):
yaml_loader = Loader
# 省略其余定义
# Python 2
class YAMLObject(object):
__metaclass__ = YAMLObjectMetaclass
yaml_loader = Loader
e.g.4:
# 使用元类实现缓存 https://www.jianshu.com/p/863dd50390aa
class InstCache(type):
_instances = {} #元类属性
def __call__(cls, *args, **kwargs):
_kw = dict(sorted(kwargs.items()))
_key = (*args, *_kw.values())
if _key not in cls._instances:
cls._instances[_key] = super().__call__(*args, **kwargs)
# cls._instances[_key] = type.__call__(cls, *args, **kwargs)
pprint('__call__', **locals())
return cls._instances[_key]
class S(metaclass=InstCache):
def __init__(self, name, age=18):
self.name = name
self.age = age
x = S('tom', 19)
y = S('tom', age=19)
print(x, y, x is y)
# 类方法调用次数
from functools import wraps
class FuncCalls(type):
@staticmethod
def call_counter(func):
@wraps(func)
def wrapper(*args, **kwargs):
wrapper.calls += 1
return func(*args, **kwargs)
wrapper.calls = 0
return wrapper
def __new__(cls, clsname, superclasses, attributedict):
for attr, value in attributedict.items():
if callable(value) and not attr.startswith('__'):
value = cls.call_counter(value) #相当于装饰器
attributedict[attr] = value
return super().__new__(cls, clsname, superclasses, attributedict)
class A(metaclass=FuncCalls):
def foo(self):
pass
a = A()
a.foo()
print(a.foo.calls)
a.foo()
print(a.foo.calls)
# output
1
2
e.g.5:
https://segmentfault.com/a/1190000011201579
编写测试类
import unittest
def calc(expression):
return eval(expression)
def add_test(name, asserts):
def test_method(asserts):
def fn(self):
left, right = asserts.split("=")
expects = str(calc(left))
self.assertEqual(expects, right)
return fn
d = {"test1": test_method(asserts)}
cls = type(name, (unittest.TestCase,), d)
globals()[name] = cls
if __name__ == '__main__':
for i, t in enumerate(["1+2=3", "3*5*6=90"]):
print(i,t)
add_test("Test%d" % i, t)
unittest.main()
其他:
https://www.jianshu.com/p/224ffcb8e73e
https://www.cnblogs.com/yssjun/p/9832526.html