社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

分享下自己对python的metaclass的知识

Py站长 • 10 年前 • 2469 次点击  

分享下自己对python的metaclass的知识。

一 你可以从这里获取什么?

1. 也许你在阅读别人的代码的时候碰到过metaclass,那你可以参考这里的介绍。
2. 或许你需要设计一些底层的库,也许metaclass能帮你简化你的设计(也有可能复杂化:)
3. 也许你在了解metaclass的相关知识之后,你对python的类的一些机制会更了解。
4. more......

二 metaclass的作用是什么?(感性认识)

metaclass能有什么用处,先来个感性的认识:

1. 你可以自由的、动态的修改/增加/删除 类的或者实例中的方法或者属性
2. 批量的对某些方法使用decorator,而不需要每次都在方法的上面加入@decorator_func
3. 当引入第三方库的时候,如果该库某些类需要patch的时候可以用metaclass
4. 可以用于序列化(参见yaml这个库的实现,我没怎么仔细看)
5. 提供接口注册,接口格式检查等
6. 自动委托(auto delegate)
7. more...

三 metaclass的相关知识

1. what is metaclass?

1.1 在wiki上面,metaclass是这样定义的:In object-oriented programming,

a metaclass is a class whose instances are classes. Just as an ordinary class defines the behavior of certain objects, a metaclass defines the behavior of certain classes and their instances.

也就是说metaclass的实例化结果是类,而class实例化的结果是instance。我是这么理解的: metaclass是类似创建类的模板,所有的类都是通过他来create的(调用new),这使得你可以自由的控制 创建类的那个过程,实现你所需要的功能。

1.2 metaclass基础

  • 一般情况下, 如果你要用类来实现metaclass的话,该类需要继承于type,而且通常会重写type的new方法来控制创建过程。 当然你也可以用函数的方式(下文会讲)
  • 在metaclass里面定义的方法会成为类的方法,可以直接通过类名来调用

2. 如何使用metaclass

2.1 用类的形式

2.1.1 类继承于type, 例如: class Meta(type):pass

2.1.2 将需要使用metaclass来构建class的类的metaclass属性(不需要显示声明,直接有的了)赋值为Meta(继承于type的类)

2.2 用函数的形式

2.2.1 构建一个函数,例如叫metaclass_new, 需要3个参数:name, bases, attrs,

name: 类的名字 bases: 基类,通常是tuple类型 attrs: dict类型,就是类的属性或者函数

2.2.2 将需要使用metaclass来构建class的类的metaclass属性(不需要显示声明,直接有的了)赋值为函数metaclas_new

3 metaclass 原理

3.1 basic

metaclass的原理其实是这样的:当定义好类之后,创建类的时候其实是调用了type的new方法为这个类分配内存空间,创建好了之后再调用type的init方法初始化(做一些赋值等)。所以metaclass的所有magic其实就在于这个new方法里面了。 说说这个方法:new(cls, name, bases, attrs)

cls: 将要创建的类,类似与self,但是self指向的是instance,而这里cls指向的是class name: 类的名字,也就是我们通常用类名.name获取的。 bases: 基类 attrs: 属性的dict。dict的内容可以是变量(类属性),也可以是函数(类方法)。

所以在创建类的过程,我们可以在这个函数里面修改name,bases,attrs的值来自由的达到我们的功能。这里常用的配合方法是 getattr和setattr(just an advice)

3.2 查找顺序

再说说关于metaclass这个属性。这个属性的说明是这样的: This variable can be any callable accepting arguments for name, bases, and dict. Upon class creation, the callable is used instead of the built-in type(). New in version 2.2.(所以有了上面介绍的分别用类或者函数的方法)

The appropriate metaclass is determined by the following precedence rules: If dict['metaclass'] exists, it is used. Otherwise, if there is at least one base class, its metaclass is used (this looks for a class attribute first and if not found, uses its type). Otherwise, if a global variable named metaclass exists, it is used. Otherwise, the old-style, classic metaclass (types.ClassType) is used.

这个查找顺序也比较好懂,而且利用这个顺序的话,如果是old-style类的话,可以在某个需要的模块里面指定全局变量 metaclass = type就能把所有的old-style 变成 new-style的类了。(这是其中一种trick)

四 例子

针对第二点说的metaclass的作用,顺序来给些例子:

  1. 你可以自由的、动态的修改/增加/删除 类的或者实例中的方法或者属性
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    #!/usr/bin/python  
    #coding :utf-8
    
    def ma(cls):  
        print 'method a'
    
    def mb(cls):  
        print 'method b'
    
    method_dict = {  
        'ma': ma,  
        'mb': mb,  
    }
    
    class DynamicMethod(type):  
        def __new__(cls, name, bases, dct):  
            if name[:3] == 'Abc':  
                dct.update(method_dict)  
            return type.__new__(cls, name, bases, dct)
    
        def __init__(cls, name, bases, dct):  
            super(DynamicMethod, cls).__init__(name, bases, dct)
    
    class AbcTest(object):  
        __metaclass__ = DynamicMethod  
        def mc(self, x):  
            print x * 3
    
    class NotAbc(object):  
        __metaclass__ = DynamicMethod  
        def md(self, x):  
            print x * 3
    
    def main():  
        a = AbcTest()  
        a.mc(3)  
        a.ma()  
        print dir(a)
    
        b = NotAbc()  
        print dir(b)
    
    if __name__ == '__main__':  
        main()
    

通过DynamicMethod这个metaclass的原型,我们可以在那些指定了metaclass属性位DynamicMethod的类里面,

根据类名字,如果是以'Abc'开头的就给它加上ma和mb的方法(这里的条件只是一种简单的例子假设了,实际应用上 可能更复杂),如果不是'Abc'开头的类就不加. 这样就可以打到动态添加方法的效果了。其实,你也可以将需要动态 添加或者修改的方法改到new里面,因为python是支持在方法里面再定义方法的. 通过这个例子,其实可以看到 只要我们能操作new方法里面的其中一个参数attrs,就可以动态添加东西了。

  1. 批量的对某些方法使用decorator,而不需要每次都在方法的上面加入@decorator_func 这个其实有应用场景的,就是比如我们cgi程序里面,很多需要验证登录或者是否有权限的,只有验证通过之后才 可以操作。那么如果你有很多个操作都需要这样做,我们一般情况下可以手动在每个方法的前头用@login_required 类似这样的方式。那如果学习了metaclass的使用的话,这次你也可以这样做:
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    #!/usr/bin/python
    #coding :utf-8
    from types import FunctionType
    
    def login_required(func):
        print 'login check logic here'
        return func
    
    class LoginDecorator(type):
        def __new__(cls, 
    
    
        
    name, bases, dct):
            for name, value in dct.iteritems():
                if name not in ('__metaclass__', '__init__', '__module__') and\
                    type(value) == FunctionType:
                    value = login_required(value)
    
                dct[name] = value
            return type.__new__(cls, name, bases, dct)
    
    class Operation(object):
        __metaclass__ = LoginDecorator
    
        def delete(self, x):
            print 'deleted %s' % str(x)
    
    def main():
        op = Operation()
        op.delete('test')
    
    if __name__ == '__main__':
        main()
    

这样子你就可以不用在delete函数上面写@login_required, 也能达到decorator的效果了。不过可读性就差点了。

  1. 当引入第三方库的时候,如果该库某些类需要patch的时候可以用metaclass

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    #!/usr/bin/python  
    #coding :utf-8
    
    def monkey_patch(name, bases, dct):  
        assert len(bases) == 1  
        base = bases[0]  
        for name, value in dct.iteritems():  
            if name not in ('__module__', '__metaclass__'):  
                setattr(base, name, value)  
        return base
    
    class A(object):  
        def a(self):  
            print 'i am A object'
    
    class PatchA(A):  
        __metaclass__ = monkey_patch
    
        def patcha_method(self):  
            print 'this is a method patched for class A'
    
    def main():  
        pa = PatchA()  
        pa.patcha_method()  
        pa.a()  
        print dir(pa)  
        print dir(PatchA)
    
    if __name__ == '__main__':  
        main()
    
  2. 提供接口注册,接口格式检查等, 这个功能可以参考这篇文章:

http://mikeconley.ca/blog/2010/05/04/python-metaclasses-in-review-board/

  1. 自动委托(auto delegate)

以下是网上的例子:

http://django-china.cn/topic/930/

五 总结

  1. metaclass的使用原则:

If you wonder whether you need them, you don't (the people who actually need them know with certainty that they need them, and don't need an explanation about why). --Tim Peters 也就是说如果你不知道能用metaclass来干什么的话,你尽量不要用,因为通常metaclass的代码会增加代码的复杂度, 降低代码的可读性。所以你必需权衡metaclass的利弊。

  1. metaclass的优势在于它的动态性和可描述性(比如上面例子中的self.delegate.getitem(i)这样的代码,它 可以用另外的函数代码生成,而无需每次手动编写), 它能把类的动态性扩展到极致。
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/931
 
2469 次点击