• python黑魔法 -- 内置方法使用


    很多pythonic的代码都会用到内置方法,根据自己的经验,罗列一下自己知道的内置方法。

    __getitem__ __setitem__ __delitem__

    这三个方法是字典类的内置方法,分别对应于查找、设置、删除操作,以一个简单的例子说明:

    class A(dict):
        def __getitem__(self, key):
            print '__getitem__'
            return super(A, self).__getitem__(key)
    
        def __setitem__(self, key, value):
            print '__setitem__'
            return super(A, self).__setitem__(key, value)
    
        def __delitem__(self, key):
            print '__delitem__'
            return super(A, self).__delitem__(key)
    
    a = A()
    a[1] = 1
    b = a[1]
    del a[1]
    a.get(1)
    

    上面的代码中a[1] = 1实际上调用的就是a.__setitem__(1, 1), a[1]调用的是a.__get__item__(1), del a[1]调用的是a.__setitem__(1)。需要注意的是,字典示例的get方法和__getitem__方法不存在调用关系,两者互不影响。

    __getattribute__ __getattr__ __setattr__ __delattr__

    __getattribute__和__getattr__都是从python对象示例中获取成员变量的方法,差别在于__getattribute__在任何时候都会调用,而__getattr__只有在__getattribute__执行完成之后并且没有找到成员变量的时候才会执行。__setattr__在给成员变量赋值的时候调用,__delattr__在回收成员变量的时候调用,一下面的例子说明:

    class A(object):
        x = []
    
        def __getattribute__(self, name):
            print '__getattribute__'
            return super(A, self).__getattribute__(name)
    
        def __setattr__(self, key, value):
            print '__setattr__'
            return super(A, self).__setattr__(key, value)
    
        def __getattr__(self, item):
            print '__getattr__'
    
        def __delattr__(self, item):
            print '__delattr__'
            return super(A, self).__delattr__(item)
    
    a = A()
    a.x
    a.y
    b = getattr(a, 'x')
    b = getattr(a, 'y')
    a.x = 1
    a.y = 1
    setattr(a, 'x', 1)
    setattr(a, 'y', 1)
    del a.x
    del a.y
    

    因为x是a的成员变量,a.x会调用a.__getattribue__('x'),而y不是a的成员变量,在调用a.__getattribue__('y')之后还会调用a.__getattr__('y'),内置方法getattr也是按照此顺序调用,唯一的区别在于getattr在成员变量不存在的时候不会抛出异常,而是给一个默认值。a.x = 1setattr(a, 'x', 1)都会调用a.__setattr__('x', 1),如果原来的成员变量不存在,__setattr__会给实例增加一个成员变量,而不是抛出异常。

    __call__

    如果重载了__call__方法,则实例对象就可以像方法一样调用了。如果实例a的类实现了这个方法,那么a(*args, **kwargs)就会调用a.__call__(*args, **kwargs)。用这种方法可以简单方便的实现装饰器,如下所示:

    class A(object):
        def __init__(self, func):
            self.func = func
    
        def __call__(self, *args, **kwargs):
            print '__call__'
            return self.func(*args, **kwargs)
    
    @A
    def foo(x):
        return x
    
    foo(1)
    

    装饰器语法糖相当于foo = A(foo), 和闭包不同,foo已经不是一个函数而是类A的实例。foo(1)会执行foo.__call__(1)。当然这个例子实现的装饰器并不好,会改变foo的函数签名,而且也不能装饰类方法。

    此外元类的__call__也可以控制实例的创建过程,因为当类创建对象时,元类的__call__函数就被调用,进而调用type.__call__创建对象,type.__call__回依次调用类的__new__和__init__生成实例。以单例模式为例:

    class Singleton(type):
        def __call__(cls, *args):
            print "Singleton call"
            if not hasattr(cls, 'instance'):
                cls.instance = super(Singleton, cls).__call__(*args)
            return cls.instance
    
    class Cache(object):
    
        __metaclass__ = Singleton
    
        def __new__(cls, *args):
            print "Cache new"
            return object.__new__(cls, *args)
    
        def __init__(cls, *args, **kwargs):
            print "Cache init"
    
    a = Cache()
    b = Cache()
    print a is b
    

    __get__ __set__

    这三个方法分别对应于描述器的查询、设置、删除函数,还是以一个简单的例子来说明:

    class A(object):
    
        def __get__(self, instance, owner):
            print '__get__'
    
        def __set__(self, instance, value):
            print '__set__'
    
    
    class B(object):
        a = A()
    
    b = B()
    c = b.a
    b.a = 1
    

    类A的示例a作为b的成员变量时候,访问示例b中成员变量a的时候会调用a.__get__(b, B),修改b中成员变量a的时候会调用a.__set__(b, 1)。需要注意的是,当用B.a访问的时候参数instance为None。实际上像classmethod,staticmethod这样的装饰器都是通过装饰器来实现的,通过装饰器就可以解决上面不能作为类方法装饰器的问题。

    import types
    
    class Profiled(object):
        def __init__(self, func):
            self.func = func
    
        def __call__(self, *args, **kwargs):
            return self.func(*args, **kwargs)
    
        def __get__(self, instance, owner):
            if instance is None:
                return self
            else:
                return types.MethodType(self, instance)
    
    
    class Spam(object):
        @Profiled
        def bar(self, x):
            return x + 1
    
    s = Spam()
    print s.bar(5)
    

    在__get__方法中,不是Spam.bar调用,则instance不为None的情况下,会用types.MethodType把bar和s做一个绑定,s.bar(5)等价于执行了bar.__get__(s, Spam).__call__(5),看起来很绕,但实际上用起来很方便。

  • 相关阅读:
    【转】java正则表达式
    NDK学习笔记-使用现有so动态库
    NDK学习笔记-增量更新
    NDK学习笔记-增量更新
    NDK学习笔记-文件的拆分与合并
    NDK学习笔记-文件的拆分与合并
    NDK学习笔记-NDK开发流程
    NDK学习笔记-NDK开发流程
    NDK学习笔记-JNI的引用
    NDK学习笔记-JNI的引用
  • 原文地址:https://www.cnblogs.com/lrysjtu/p/5665422.html
Copyright © 2020-2023  润新知