python-元类2

定义: 创建对象的对象(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
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值