zoukankan      html  css  js  c++  java
  • python 3 黑色魔法元类初探

    最近读django源码,发现必须了解元类才能理解一些很神奇的行为.

    发现元类实际上是控制class的创建过程.比如类B继承某个看似平淡无奇的类A之后,你在类B中定义的属性或方法可能会遭到彻底改变.

    假设我们想实现这么一种需求:

    创建一个类Child,在这个类中定义的种种字符串属性,都可以当做对应的函数那样调用.例如:

    class Child():

        f1='print'

    随后,Child.f1就完全和内建函数print等价了.Child.f1('abc')能够打印出abc这个字符串.

    这样来说,元类的用途看起来可以是:一些高手设计了一个在幕后做了很多工作的元类,另一些开发人员只需要简单地继承这个元类或者这个元类的子类,敲几个属性定义的代码,就能实现很多复杂的功能.django就是这样的.比如它的model.Models就充分利用了这种魔法.

    下面从代码层面展示元类具体是怎么运作的:

    class MyMeta(type):    
        def __new__(cls, name, parents, attrs):
            for k,v in locals().items():            
                if isinstance(v,dict):
                    for k2,v2 in v.items():
                        print(k,'|',k2,'|',type(v2),'|',v2)
                else:
                    print(k,'|',v)            
            return type.__new__(cls, name, parents, attrs)  
    
    class Parent1(type):
        pass
    
    class Parent2(object):
        pass
    
    class Parent3():
        pass
    
    class AbstractChild(Parent1,Parent2,Parent3,metaclass=MyMeta):
        pass
    
    class Child(AbstractChild):
        
        f1 = 1
        f2 = sorted
        f3 = int
        
        class classinchild():
            pass
    
        @staticmethod
        def stcm():
            pass
        
        @classmethod
        def clsm(cls):
            pass
        
        def insm(self):
            pass
    
    print('-'*60)
    
    class EvalMeta(type):
        def __new__(cls, name, parents, attrs):
            new_attrs={}
            for k,v in attrs.items():            
                if not k.startswith('__') and isinstance(v,str):
                    new_attrs[k] = eval(v)
                else:
                    new_attrs[k] = v           
            return type.__new__(cls, name, parents, new_attrs)
    
    class AbstractChild(metaclass=EvalMeta):
        pass
    
    class Child(AbstractChild):
        f1='print'
        f2='sorted'
    
    if __name__=='__main__':
        x=Child()
        x.f1('invoke f1 as print function')
        print('invoke f2 as sorted function:',Child.f2([3,2,1]))

    结果:

    >>> 
    name | AbstractChild
    cls | <class '__main__.MyMeta'>
    parents | (<class '__main__.Parent1'>, <class '__main__.Parent2'>, <class '__main__.Parent3'>)
    attrs | __module__ | <class 'str'> | __main__
    attrs | __qualname__ | <class 'str'> | AbstractChild
    name | Child
    cls | <class '__main__.MyMeta'>
    parents | (<class '__main__.AbstractChild'>,)
    attrs | stcm | <class 'staticmethod'> | <staticmethod object at 0x0000000003075C50>
    attrs | clsm | <class 'classmethod'> | <classmethod object at 0x000000000309CA58>
    attrs | __module__ | <class 'str'> | __main__
    attrs | insm | <class 'function'> | <function Child.insm at 0x0000000003139400>
    attrs | __qualname__ | <class 'str'> | Child
    attrs | f1 | <class 'int'> | 1
    attrs | f3 | <class 'type'> | <class 'int'>
    attrs | f2 | <class 'builtin_function_or_method'> | <built-in function sorted>
    attrs | classinchild | <class 'type'> | <class '__main__.Child.classinchild'>
    ------------------------------------------------------------
    invoke f1 as print function
    invoke f2 as sorted function: [1, 2, 3]
    >>> 
  • 相关阅读:
    第七章-方法区
    wchar_t 字符拼接
    C++获取appdata路径
    char * 、BSTR、long、wchar_t *、LPCWSTR、string、QString类型转换
    climits 与 符号常量
    Qt数据结构-QString二:QString的arg能不能像Python的format一样使用
    Qt数据结构-QString一:常用方法
    怎么查看摄像头的硬件ID
    jenkins提示使用java11版本
    Jenkins:the input device is not a TTY
  • 原文地址:https://www.cnblogs.com/xiangnan/p/3508441.html
Copyright © 2011-2022 走看看