python的魔法方法


魔法方法 调用时机 说明
__new__(cls, *args) instance = Class(arg1, arg2) __new__产生的实例也就是__init__里面的的self
__init__(self, *args) instance = Class(arg1, arg2) __init__在实例创建时调用
__call__(self, *args) Class(arg1, arg2) __call__允许类的实例跟函数一样表现
__eq__(self, other) self == other 比较操作符
__ne__(self, other) self != other 比较操作符
__lt__(self, other) self < other 比较操作符
__gt__(self, other) self > other 比较操作符
__le__(self, other) self <= other 比较操作符
__ge__(self, other) self >= other 比较操作符
__pos__(self) +self 取正(一元操作符)
__neg__(self) -self 取负(一元操作符)
__abs__(self) abs(self) 绝对值(一元操作符)
__invert__(self) !self 取反(一元操作符)
__add__(self, other) self+other 加法(算数操作符)
__sub__(self, other) self-other 减法(算数操作符)
__mul__(self, other) self*other 乘法(算数操作符)
__floordiv__(self, other) self//other 整除(算数操作符)
__truediv__(self, other) self/other 除法(算数操作符)
__mod__(self, other) self%other 取余(算数操作符)
__pow__(self, other) self**other 幂运算(算数操作符)
__and__(self, other) self&other 与运算(算数操作符)
__or__(self, other) `self other` 或运算(算数操作符)
__iadd__(self, other) self+=other 赋值操作符
__isub__(self, other) self-=other 赋值操作符
__imul__(self, other) self*=other 赋值操作符
__ifloordiv__(self, other) self//=other 赋值操作符
__itruediv__(self, other) self/=other 赋值操作符
__imod__(self, other) self%=other 赋值操作符
__str__(self) str(self)
__len__(self) len(self)

__init____new__最主要的区别在于:

  • __init__通常用于初始化一个新实例,控制这个初始化的过程,比如添加一些属性,做一些额外的操作,发生在类实例被创建完以后。它是实例级别的方法。
  • __new__通常用于控制生成一个新实例的过程。它是类级别的方法。

__new__的作用

__new__方法主要是当你继承一些不可变的class时(比如int,str,tuple), 提供给你一个自定义这些类的实例化过程的途径。

class PositiveInteger(int):
    def __new__(cls, val):
        return super(PositiveInteger, cls).__new__(cls, abs(val));

__new__来实现单例

class Singleton(object):
    def __new__(cls):
        if not hasattr(cls, "instance"):
            cls.instance = super(Singleton, cls).__new__(cls);
        return cls.instance;

属性访问控制

__getattr__(self, name)

  • 该方法定义了你试图访问一个不存在的属性时的行为。因此,重载该方法可以实现捕获错误拼写然后进行重定向, 或者对一些废弃的属性进行警告。

__setattr__(self, name, value)

  • __setattr__是实现封装的解决方案,它定义了你对属性进行赋值和修改操作时的行为。
  • 不管对象的某个属性是否存在,它都允许你为该属性进行赋值,因此你可以为属性的值进行自定义操作。有一点需要注意,实现__setattr__时要避免"无限递归"的错误。

__delattr__(self, name)

  • __delattr____setattr__很像,只是它定义的是你删除属性时的行为。实现__delattr__是同时要避免"无限递归"的错误。

__getattribute__(self, name)

  • __getattribute__定义了你的属性被访问时的行为,相比较,__getattr__只有该属性不存在时才会起作用。
  • 因此,在支持__getattribute__的Python版本,调用__getattr__前必定会调用 __getattribute____getattribute__同样要避免"无限递归"的错误。
class Foo(object):
    def __init__(self, value):
        self.value = value

    def __getattr__(self, item):
        if item == 'scolia':
            return 'no attr:%s' % item
        elif item in self.__dict__:
            return self.__dict__[item]
        else:
            raise AttributeError('no attr:%s' % item)

    def __setattr__(self, key, value):
        if key == 'good':
            print 'can not set the attr: good'
        else:
            self.__dict__[key] = value

    def __delattr__(self, item):
        if item == 'a':
            print 'no attr: good'
        else:
            del self.__dict__[item]

    def __getattribute__(self, item):
        if item == 'a':
            raise AttributeError('not a')
        return object.__getattribute__(self, item)

描述器

一个类要成为描述器,必须实现__get__, __set__, __delete__中的至少一个方法。下面简单介绍下:

__get__(self, instance, owner)

  • 参数instance是拥有者类(指调用该描述器的类)的实例。参数owner是拥有者类本身。__get__在其拥有者对其读值的时候调用。

__set__(self, instance, value)

  • __set__在其拥有者对其进行修改值的时候调用。

__delete__(self, instance)

  • __delete__在其拥有者对其进行删除的时候调用。

构造自定义容器

在Python中,常见的容器类型有: dict, tuple, list, string。
其中tuple, string是不可变容器,dict, list是可变容器。

  • 如果要自定义不可变容器类型,只需要定义__len____getitem__方法;
  • 如果要自定义可变容器类型,还需要在不可变容器类型的基础上增加定义__setitem____delitem__;
  • 如果你希望你的自定义数据结构还支持"可迭代", 那就还需要定义__iter__

__len__(self)

  • 需要返回数值类型,以表示容器的长度。该方法在可变容器和不可变容器中必须实现。

__getitem__(self, key)

  • 当你执行self[key]的时候,调用的就是该方法。该方法在可变容器和不可变容器中也都必须实现。
  • 调用的时候,如果key的类型错误,该方法应该抛出TypeError;
  • 如果没法返回key对应的数值时,该方法应该抛出ValueError。

__setitem__(self, key, value)

  • 当你执行self[key] = value时,调用的是该方法。

__delitem__(self, key)

  • 当你执行del self[key]的时候,调用的是该方法。

__iter__(self)

  • 该方法需要返回一个迭代器(iterator)。当你执行for x in container: 或者使用iter(container)时,该方法被调用。

__reversed__(self)

  • 如果想要该数据结构被內建函数reversed()支持,就还需要实现该方法。

__contains__(self, item)

  • 如果定义了该方法,那么在执行item in container 或者 item not in container时该方法就会被调用。
  • 如果没有定义,那么Python会迭代容器中的元素来一个一个比较,从而决定返回True或者False。

上下文管理

参考Python 上下文管理器

with open("test.txt") as f:
    print(f.read());

需要实现两个魔术方法: __enter____exit__

__enter__(self)

  • __enter__会返回一个值,并赋值给as关键词之后的变量。在这里,你可以定义代码段开始的一些操作。

__exit__(self, exception_type, exception_value, traceback)

  • __exit__定义了代码段结束后的一些操作,可以这里执行一些清除操作,或者做一些代码段结束后需要立即执行的命令,比如文件的关闭,socket断开等。如果代码段成功结束,那么exception_type, exception_value, traceback 三个参数传进来时都将为None。如果代码段抛出异常,那么传进来的三个参数将分别为: 异常的类型,异常的值,异常的追踪栈。
  • 如果__exit__返回True, 那么with声明下的代码段的一切异常将会被屏蔽。
  • 如果__exit__返回None, 那么如果有异常,异常将正常抛出,这时候with的作用将不会显现出来。

python中字典的key要求

  • __hash__方法可以做字典的key,没有则不能作为字典的key;
  • 除了list、dict、set和内部至少带有上述三种类型之一的tuple之外,其余对象均可作为字典的key。
class Mylist(list):
def __hash__(self):
    print("hash(self(0)) = ",hash(self[0]))
    return hash(self[0])

l = Mylist([1,2])
d = {}
d[l] = l # hash(self(0)) =  1
print(d) # {[1, 2]: [1, 2]}
d[1] = 1
print(d) # {[1, 2]: [1, 2], 1: 1}

在将对象作为字典的key时,__hash__会先执行,找到对应的内存,没有值则直接写入;有值,判断新来的键和原来的键是否相等,相等则认为是一个键,更新值,不相等则开辟空间,自增键值对。

class Mylist(list):
    def __hash__(self):
        print("hash(self(0)) = ",hash(self[0]))
        print("hash running.")
        return hash(self[0])

    def __eq__(self,other):
        print("eq running.")
        return self[0] == other

def test1():
    l = Mylist([1,2])
    d = {}
    d[l] = l
    print(d)
    d[1] = 1
    print(d)
    # 输出:
    # hash(self(0)) =  1
    # hash running.
    # {[1, 2]: [1, 2]}
    # eq running.
    # {[1, 2]: 1}

def test2():
    l = Mylist([1,2])
    d = {}
    d[1] = 1
    print(d)
    d[l] = l
    print(d)
    # 输出:
    # {1: 1}
    # hash(self(0)) =  1
    # hash running.
    # eq running.
    # {1: [1, 2]}

results matching ""

    No results matching ""