>  기사  >  백엔드 개발  >  Python의 매직 메소드 소개

Python의 매직 메소드 소개

巴扎黑
巴扎黑원래의
2017-03-18 11:38:121334검색

머리말

파이썬에서는 "__" 이중 밑줄로 묶인 모든 메소드를 통칭하여 "매직 메소드"라고 합니다. 예를 들어 __init__.

우리가 가장 많이 접하게 되는 몇 가지 마술 방법은 평생 다시는 접하지 못할 수도 있으므로 간단히 소개하겠습니다.

그리고 일부 마법의 방법은 독창적입니다. 이를 사용하여 복잡한 논리를 간단한 API로 캡슐화하는 등 매우 아름다운 코드를 구성할 수 있습니다.

이 글의 편집 아이디어는 Rafe Kettler의 블로그: A Guide to Python Magic Methods에서 차용했으며, 일부 코드 예제가 추가되었습니다.

소개 순서는 대략적으로 일반적인 것이 먼저 소개되고, 희귀한 것이 나중에 소개됩니다.

이 기사에 사용된 코드 예제는 내 github에서 다운로드할 수 있습니다.

생성 및 초기화

__init__객체가 초기화될 때 호출되는 것을 일반적으로 "생성자"라고 이해합니다. 실제로

을 호출하면

가 가장 먼저 실행되는 것이 아니고 x = SomeClass()이 실행됩니다. 정확하게 말하면 __init____new__는 함께 "생성자"를 구성합니다. __new____init__

은 클래스를 만들고 이 클래스의 인스턴스를 반환하는 데 사용되며

는 매개변수만 전달합니다. 인스턴스를 초기화하려면 __new____init__

이 인스턴스 생성 과정에서 호출되지만, 예를 들어

__new__을 통해 인스턴스를 역직렬화하는 경우에는 __init__이 호출되지 않습니다. 호출되지 않습니다. pickle.load__init__

메서드는 항상 클래스의 인스턴스를 반환해야 하지만

는 None을 제외한 어떤 값도 반환할 수 없습니다. 예를 들어, 다음 예는 __new__

class Foo(object):

    def __init__(self):
        print 'foo __init__'
        return None  # 必须返回None,否则抛TypeError

    def __del__(self):
        print 'foo __del__'
__init__실제로 클래스 생성을 제어하려는 경우가 아니면

를 거의 사용하지 않습니다.

__new__을 설명하려면
(메타클래스)를 소개하는 경우가 많습니다. __new__더 깊이 알고 싶다면 내 다른 블로그인 Python의 메타클래스 이해를 참조하세요.metaclass

의 오버로딩에 대해서는 Python 문서에 자세한 소개도 있습니다.

__new__객체 수명 주기가 끝나면

이 호출됩니다.

는 "소멸자"로 이해될 수 있습니다. __del____del__은 객체가 가비지 수집 동작인 경우를 정의합니다.
__del__은 좀 오해하기 쉽습니다. 사실

의 구현이 아니지만, x.__del__()을 실행할 때 del x이 자주 호출되는데요. 위의 Foo 클래스 코드를 예시로 계속 사용하세요. del x

foo = Foo()
foo.__del__()
print foo
del foo
print foo  # NameError, foo is not defined
x.__del__()

가 호출되면 객체 자체는 여전히 존재합니다. 그러나

가 호출되면 객체가 없습니다. foo 더 이상 .

인터프리터가 종료될 때 객체가 여전히 존재하는 경우 foo.__del__()가 정확하게 실행된다는 보장은 없습니다. 따라서 del foo은 좋은 프로그래밍 습관을 대체할 수 없습니다.

예를 들어 소켓을 처리할 때 적시에 완료된 연결을 닫습니다.

__del__속성 액세스 제어__del__
Python의 클래스 캡슐화가 부족하다고 불평하는 사람들이 항상 있습니다. 예를 들어 Python이 비공개 속성을 정의한 다음 공개적으로 액세스 가능한 getter 및 setter를 제공할 수 있기를 바랍니다. Python은 실제로 매직 메소드를 통해 캡슐화를 달성할 수 있습니다.

이 메서드는 존재하지 않는 속성에 액세스하려고 할 때의 동작을 정의합니다. 따라서 이 메서드를 오버로드하면 맞춤법 오류를 찾아 리디렉션하거나 더 이상 사용되지 않는 일부 속성에 대해 경고할 수 있습니다.

__getattr__(self, name)

은 속성을 할당하고 수정할 때의 동작을 정의하는 캡슐화를 구현하는 솔루션입니다. __setattr__(self, name, value)객체의 특정 속성이 존재하는지 여부에 관계없이 속성에 값을 할당할 수 있으므로 속성 값에 대해 사용자 정의 작업을 수행할 수 있습니다. 한 가지 주의할 점은

구현 시 "무한 재귀" 오류를 방지하는 것입니다. 이에 대해서는 아래 코드 예제에서 언급하겠습니다.

__setattr__
__setattr__

은 속성을 삭제할 때의 동작을 정의한다는 점을 제외하면 __delattr__(self, name)과 매우 유사합니다.

을 구현하는 것은 "무한 재귀" 오류를 동시에 방지하기 위한 것입니다.

__delattr____setattr____delattr__

은 속성에 액세스할 때의 동작을 정의합니다. 반면 __getattribute__(self, name)은 속성이 존재하지 않는 경우에만 작동합니다.

따라서 __getattribute__을 지원하는 Python 버전에서는 __getattr__을 호출하기 전에

을 사용해야 합니다.

'무한 재귀' 오류도 방지하세요. __getattribute____getattr____getattribute__을 구현하려고 하지 않는 것이 가장 좋다는 점을 기억해야 합니다. 이 접근 방식은 거의 볼 수 없고 버그를 일으키기 쉽기 때문입니다. __getattribute__

예제는

:__getattribute__

def __setattr__(self, name, value):
    self.name = value
    # 每一次属性赋值时, __setattr__都会被调用,因此不断调用自身导致无限递归了。

의 무한 재귀 오류를 보여줍니다. 따라서 올바른 작성 방법은 다음과 같습니다.

def __setattr__(self, name, value):
    self.__dict__[name] = value

__delattr__如果在其实现中出现del self.name 这样的代码也会出现”无限递归”错误,这是一样的原因。

下面的例子很好的说明了上面介绍的4个魔术方法的调用情况:

class Access(object):

    def __getattr__(self, name):
        print '__getattr__'
        return super(Access, self).__getattr__(name)

    def __setattr__(self, name, value):
        print '__setattr__'
        return super(Access, self).__setattr__(name, value)

    def __delattr__(self, name):
        print '__delattr__'
        return super(Access, self).__delattr__(name)

    def __getattribute__(self, name):
        print '__getattribute__'
        return super(Access, self).__getattribute__(name)

access = Access()
access.attr1 = True  # __setattr__调用
access.attr1  # 属性存在,只有__getattribute__调用
try:
    access.attr2  # 属性不存在, 先调用__getattribute__, 后调用__getattr__
except AttributeError:
    pass
del access.attr1  # __delattr__调用

描述器对象

我们从一个例子来入手,介绍什么是描述符,并介绍__get____set____delete__ 的使用。(放在这里介绍是为了跟上一小节介绍的魔术方法作对比)

我们知道,距离既可以用单位”米”表示,也可以用单位”英尺”表示。现在我们定义一个类来表示距离,它有两个属性: 米和英尺。

class Meter(object):
    '''Descriptor for a meter.'''
    def __init__(self, value=0.0):
        self.value = float(value)
    def __get__(self, instance, owner):
        return self.value
    def __set__(self, instance, value):
        self.value = float(value)

class Foot(object):
    '''Descriptor for a foot.'''
    def __get__(self, instance, owner):
        return instance.meter * 3.2808
    def __set__(self, instance, value):
        instance.meter = float(value) / 3.2808

class Distance(object):
    meter = Meter()
    foot = Foot()

d = Distance()
print d.meter, d.foot  # 0.0, 0.0
d.meter = 1
print d.meter, d.foot  # 1.0 3.2808
d.meter = 2
print d.meter, d.foot  # 2.0 6.5616

在上面例子中,在还没有对Distance的实例赋值前, 我们认为meter和foot应该是各自类的实例对象, 但是输出却是数值。这是因为__get__发挥了作用.

我们只是修改了meter,并且将其赋值成为int,但foot也修改了。这是__set__发挥了作用.

描述器对象(Meter、Foot)不能独立存在, 它需要被另一个所有者类(Distance)所持有。

描述器对象可以访问到其拥有者实例的属性,比如例子中Foot的instance.meter

在面向对象编程时,如果一个类的属性有相互依赖的关系时,使用描述器来编写代码可以很巧妙的组织逻辑。在Django的ORM中, models.Model中的InterField等字段, 就是通过描述器来实现功能的。

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

__get__(self, instance, owner)

参数instance是拥有者类的实例。参数owner是拥有者类本身。__get__在其拥有者对其读值的时候调用。

__set__(self, instance, value)

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

__delete__(self, instance)

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

构造自定义容器(Container)

在Python中,常见的容器类型有: dict, tuple, list, string。

其中tuple, string是不可变容器,dict, list是可变容器。

可变容器和不可变容器的区别在于,不可变容器一旦赋值后,不可对其中的某个元素进行修改。

比如定义了l = [1, 2, 3]t = (1, 2, 3)后, 执行l[0] = 0是可以的,但执行t[0] = 0则会报错。

如果我们要自定义一些数据结构,使之能够跟以上的容器类型表现一样,那就需要去实现某些协议。

这里的协议跟其他语言中所谓的”接口”概念很像,一样的需要你去实现才行,只不过没那么正式而已。

如果要自定义不可变容器类型,只需要定义__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。

__missing__(self, key)

dict字典类型会有该方法,它定义了key如果在容器中找不到时触发的行为。
比如d = {'a': 1}, 当你执行d[notexist]时,d.__missing__['notexist']就会被调用。

下面举例,使用上面讲的魔术方法来实现Haskell语言中的一个数据结构。

# -*- coding: utf-8 -*-
class FunctionalList:
    ''' 实现了内置类型list的功能,并丰富了一些其他方法: head, tail, init, last, drop, take'''

    def __init__(self, values=None):
        if values is None:
            self.values = []
        else:
            self.values = values

    def __len__(self):
        return len(self.values)

    def __getitem__(self, key):
        return self.values[key]

    def __setitem__(self, key, value):
        self.values[key] = value

    def __delitem__(self, key):
        del self.values[key]

    def __iter__(self):
        return iter(self.values)

    def __reversed__(self):
        return FunctionalList(reversed(self.values))

    def append(self, value):
        self.values.append(value)
    def head(self):
        # 获取第一个元素
        return self.values[0]
    def tail(self):
        # 获取第一个元素之后的所有元素
        return self.values[1:]
    def init(self):
        # 获取最后一个元素之前的所有元素
        return self.values[:-1]
    def last(self):
        # 获取最后一个元素
        return self.values[-1]
    def drop(self, n):
        # 获取所有元素,除了前N个
        return self.values[n:]
    def take(self, n):
        # 获取前N个元素
        return self.values[:n]

我们再举个例子,实现Perl语言的autovivification,它会在你每次引用一个值未定义的属性时为你自动创建数组或者字典。

class AutoVivification(dict):
    """Implementation of perl's autovivification feature."""
    def __missing__(self, key):
        value = self[key] = type(self)()
        return value

weather = AutoVivification()
weather['china']['guangdong']['shenzhen'] = 'sunny'
weather['china']['hubei']['wuhan'] = 'windy'
weather['USA']['California']['Los Angeles'] = 'sunny'
print weather

结果输出:{'china': {'hubei': {'wuhan': 'windy'}, 'guangdong': {'shenzhen': 'sunny'}}, 'USA':    {'California': {'Los Angeles': 'sunny'}}}

在Python中,关于自定义容器的实现还有更多实用的例子,但只有很少一部分能够集成在Python标准库中,比如Counter, OrderedDict等

上下文管理

with声明是从Python2.5开始引进的关键词。你应该遇过这样子的代码:

with open('foo.txt') as bar:
    # do something with bar

在with声明的代码段中,我们可以做一些对象的开始操作和清除操作,还能对异常进行处理。

这需要实现两个魔术方法: __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的作用将不会显现出来。

举例说明:

这该示例中,IndexError始终会被隐藏,而TypeError始终会抛出。

class DemoManager(object):

    def __enter__(self):
        pass

    def __exit__(self, ex_type, ex_value, ex_tb):
        if ex_type is IndexError:
            print ex_value.__class__
            return True
        if ex_type is TypeError:
            print ex_value.__class__
            return  # return None

with DemoManager() as nothing:
    data = [1, 2, 3]
    data[4]  # raise IndexError, 该异常被__exit__处理了

with DemoManager() as nothing:
    data = [1, 2, 3]
    data['a']  # raise TypeError, 该异常没有被__exit__处理

输出:
<type 'exceptions.IndexError'>
<type 'exceptions.TypeError'>
Traceback (most recent call last):
  ...

对象的序列化

Python对象的序列化操作是pickling进行的。pickling非常的重要,以至于Python对此有单独的模块pickle,还有一些相关的魔术方法。使用pickling, 你可以将数据存储在文件中,之后又从文件中进行恢复。

下面举例来描述pickle的操作。从该例子中也可以看出,如果通过pickle.load 初始化一个对象, 并不会调用__init__方法。

# -*- coding: utf-8 -*-
from datetime import datetime
import pickle

class Distance(object):

    def __init__(self, meter):
        print 'distance __init__'
        self.meter = meter

data = {
    'foo': [1, 2, 3],
    'bar': ('Hello', 'world!'),
    'baz': True,
    'dt': datetime(2016, 10, 01),
    'distance': Distance(1.78),
}
print 'before dump:', data
with open('data.pkl', 'wb') as jar:
    pickle.dump(data, jar)  # 将数据存储在文件中

del data
print 'data is deleted!'

with open('data.pkl', 'rb') as jar:
    data = pickle.load(jar)  # 从文件中恢复数据
print 'after load:', data

值得一提,从其他文件进行pickle.load操作时,需要注意有恶意代码的可能性。另外,Python的各个版本之间,pickle文件可能是互不兼容的。

pickling并不是Python的內建类型,它支持所有实现pickle协议(可理解为接口)的类。pickle协议有以下几个可选方法来自定义Python对象的行为。

__getinitargs__(self)

如果你希望unpickle时,__init__方法能够调用,那么就需要定义__getinitargs__, 该方法需要返回一系列参数的元组,这些参数就是传给__init__的参数。

该方法只对old-style class有效。所谓old-style class,指的是不继承自任何对象的类,往往定义时这样表示: class A:, 而非class A(object):

__getnewargs__(self)

__getinitargs__很类似,只不过返回的参数元组将传值给__new__

__getstate__(self)

在调用pickle.dump时,默认是对象的__dict__属性被存储,如果你要修改这种行为,可以在__getstate__方法中返回一个state。state将在调用pickle.load时传值给__setstate__

__setstate__(self, state)

一般来说,定义了__getstate__,就需要相应地定义__setstate__来对__getstate__返回的state进行处理。

__reduce__(self)

如果pickle的数据包含了自定义的扩展类(比如使用C语言实现的Python扩展类)时,就需要通过实现__reduce__方法来控制行为了。由于使用过于生僻,这里就不展开继续讲解了。

令人容易混淆的是,我们知道, reduce()是Python的一个內建函数, 需要指出__reduce__并非定义了reduce()的行为,二者没有关系。

__reduce_ex__(self)

__reduce_ex__ 是为了兼容性而存在的, 如果定义了__reduce_ex__, 它将代替__reduce__ 执行。

下面的代码示例很有意思,我们定义了一个类Slate(中文是板岩的意思)。这个类能够记录历史上每次写入给它的值,但每次pickle.dump时当前值就会被清空,仅保留了历史。

# -*- coding: utf-8 -*-
import pickle
import time

class Slate:
    '''Class to store a string and a changelog, and forget its value when pickled.'''
    def __init__(self, value):
        self.value = value
        self.last_change = time.time()
        self.history = []

    def change(self, new_value):
        # 修改value, 将上次的valeu记录在history
        self.history.append((self.last_change, self.value))
        self.value = new_value
        self.last_change = time.time()

    def print_changes(self):
        print 'Changelog for Slate object:'
        for k, v in self.history:
            print '%s    %s' % (k, v)

    def __getstate__(self):
        # 故意不返回self.value和self.last_change,
        # 以便每次unpickle时清空当前的状态,仅仅保留history
        return self.history

    def __setstate__(self, state):
        self.history = state
        self.value, self.last_change = None, None

slate = Slate(0)
time.sleep(0.5)
slate.change(100)
time.sleep(0.5)
slate.change(200)
slate.change(300)
slate.print_changes()  # 与下面的输出历史对比
with open('slate.pkl', 'wb') as jar:
    pickle.dump(slate, jar)
del slate  # delete it
with open('slate.pkl', 'rb') as jar:
    slate = pickle.load(jar)
print 'current value:', slate.value  # None
print slate.print_changes()  # 输出历史记录与上面一致

运算符相关的魔术方法

运算符相关的魔术方法实在太多了,也很好理解,不打算多讲。在其他语言里,也有重载运算符的操作,所以我们对这些魔术方法已经很了解了。

比较运算符

__cmp__(self, other)

如果该方法返回负数,说明self 042dce579c75a4e6bf6232131b927758 other; 返回0说明self == other

强烈不推荐来定义__cmp__, 取而代之, 最好分别定义__lt__等方法从而实现比较功能。
__cmp__在Python3中被废弃了。

__eq__(self, other)

定义了比较操作符==的行为.

__ne__(self, other)

定义了比较操作符!=的行为.

__lt__(self, other)

定义了比较操作符ccf8deccb5da98fff4b9bc74a8c5ab3f的行为.

__le__(self, other)

定义了比较操作符64a319a9972386dadb9b47d23b11326e=的行为.

下面我们定义一种类型Word, 它会使用单词的长度来进行大小的比较, 而不是采用str的比较方式。
但是为了避免 Word('bar') == Word('foo') 这种违背直觉的情况出现,并没有定义__eq__, 因此Word会使用它的父类(str)中的__eq__来进行比较。

下面的例子中也可以看出: 在编程语言中, 如果a >=b and a 9999d93a92f2957b76a21b768be88604>.

__and__(self, other)

实现了位操作&.

__or__(self, other)

实现了位操作|

__xor__(self, other)

实现了位操作^

反算术运算符

这里只需要解释一下概念即可。假设针对some_object这个对象:

some_object + other

上面的代码非常正常地实现了some_object的__add__方法。那么如果遇到相反的情况呢?

other + some_object

这时候,如果other没有定义__add__方法,但是some_object定义了__radd__, 那么上面的代码照样可以运行。
这里的__radd__(self, other)就是__add__(self, other)的反算术运算符。

所以,类比的,我们就知道了更多的反算术运算符, 就不一一展开了:

  • __rsub__(self, other)

  • __rmul__(self, other)

  • __rmul__(self, other)

  • __rfloorp__(self, other)

  • __rp__(self, other)

  • __rtruep__(self, other)

  • __rmod__(self, other)

  • __rpmod__(self, other)

  • __rpow__(self, other)

  • __rlshift__(self, other)

  • __rrshift__(self, other)

  • __rand__(self, other)

  • __ror__(self, other)

  • __rxor__(self, other)

增量赋值

这也是只要理解了概念就容易掌握的运算。举个例子:

x = 5
x += 1  # 这里的+=就是增量赋值,将x+1赋值给了x

因此对于a += b__iadd__ 将返回a + b, 并赋值给a。
所以很容易理解下面的魔术方法了:

  • __iadd__(self, other)

  • __isub__(self, other)

  • __imul__(self, other)

  • __ifloorp__(self, other)

  • __ip__(self, other)

  • __itruep__(self, other)

  • __imod__(self, other)

  • __ipow__(self, other)

  • __ilshift__(self, other)

  • __irshift__(self, other)

  • __iand__(self, other)

  • __ior__(self, other)

  • __ixor__(self, other)

类型转化

__int__(self)

实现了类型转化为int的行为.

__long__(self)

实现了类型转化为long的行为.

__float__(self)

实现了类型转化为float的行为.

__complex__(self)

实现了类型转化为complex(复数, 也即1+2j这样的虚数)的行为.

__oct__(self)

实现了类型转化为八进制数的行为.

__hex__(self)

实现了类型转化为十六进制数的行为.

__index__(self)

在切片运算中将对象转化为int, 因此该方法的返回值必须是int。用一个例子来解释这个用法。

class Thing(object):
    def __index__(self):
        return 1

thing = Thing()
list_ = ['a', 'b', 'c']
print list_[thing]  # 'b'
print list_[thing:thing]  # []

上面例子中, list_[thing]的表现跟list_[1]一致,正是因为Thing实现了__index__方法。

可能有的人会想,list_[thing]为什么不是相当于list_[int(thing)]呢? 通过实现Thing的__int__方法能否达到这个目的呢?

显然不能。如果真的是这样的话,那么list_[1.1:2.2]这样的写法也应该是通过的。
而实际上,该写法会抛出TypeError: slice indices must be integers or None or have an __index__ method

下面我们再做个例子,如果对一个dict对象执行dict_[thing]会怎么样呢?

dict_ = {1: 'apple', 2: 'banana', 3: 'cat'}
print dict_[thing]  # raise KeyError

这个时候就不是调用__index__了。虽然listdict都实现了__getitem__方法, 但是它们的实现方式是不一样的。
如果希望上面例子能够正常执行, 需要实现Thing的__hash__ 和 __eq__方法.

class Thing(object):
    def __hash__(self):
        return 1
    def __eq__(self, other):
        return hash(self) == hash(other)

dict_ = {1: 'apple', 2: 'banana', 3: 'cat'}
print dict_[thing]  # apple

__coerce__(self, other)

实现了混合模式运算。

要了解这个方法,需要先了解coerce()内建函数: 官方文档上的解释是, coerce(x, y)返回一组数字类型的参数, 它们被转化为同一种类型,以便它们可以使用相同的算术运算符进行操作。如果过程中转化失败,抛出TypeError。

比如对于coerce(10, 10.1), 因为10和10.1在进行算术运算时,会先将10转为10.0再来运算。因此coerce(10, 10.1)返回值是(10.0, 10.1).

__coerce__在Python3中废弃了。

其他魔术方法

还没讲到的魔术方法还有很多,但有些我觉得很简单,或者很少见,就不再累赘展开说明了。

__str__(self)

对实例使用str()时调用。

__repr__(self)

对实例使用repr()时调用。str()repr()都是返回一个代表该实例的字符串,
主要区别在于: str()的返回值要方便人来看,而repr()的返回值要方便计算机看。

__unicode__(self)

对实例使用unicode()时调用。unicode()str()的区别在于: 前者返回值是unicode, 后者返回值是str。unicode和str都是basestring的子类。

当你对一个类只定义了__str__但没定义__unicode__时,__unicode__会根据__str__的返回值自动实现,即return unicode(self.__str__());
但返回来则不成立。

class StrDemo2:
    def __str__(self):
        return 'StrDemo2'

class StrDemo3:
    def __unicode__(self):
        return u'StrDemo3'

demo2 = StrDemo2()
print str(demo2)  # StrDemo2
print unicode(demo2)  # StrDemo2

demo3 = StrDemo3()
print str(demo3)  # <__main__.StrDemo3 instance>
print unicode(demo3)  # StrDemo3

__format__(self, formatstr)

"Hello, {0:abc}".format(a)等价于format(a, "abc"), 等价于a.__format__("abc")

这在需要格式化展示对象的时候非常有用,比如格式化时间对象。

__hash__(self)

对实例使用hash()时调用, 返回值是数值类型。

__nonzero__(self)

对实例使用bool()时调用, 返回True或者False。
你可能会问, 为什么不是命名为__bool__? 我也不知道。
我只知道该方法在Python3中改名为__bool__了。

__dir__(self)

对实例使用dir()时调用。通常实现该方法是没必要的。

__sizeof__(self)

对实例使用sys.getsizeof()时调用。返回对象的大小,单位是bytes。

__instancecheck__(self, instance)

对实例调用isinstance(instance, class)时调用。 返回值是布尔值。它会判断instance是否是该类的实例。

__subclasscheck__(self, subclass)

对实例使用issubclass(subclass, class)时调用。返回值是布尔值。它会判断subclass否是该类的子类。

__copy__(self)

对实例使用copy.copy()时调用。返回”浅复制”的对象。

__deepcopy__(self, memodict={})

对实例使用copy.deepcopy()时调用。返回”深复制”的对象。

__call__(self, [args...])

该方法允许类的实例跟函数一样表现:

class XClass:
    def __call__(self, a, b):
        return a + b

def add(a, b):
    return a + b

x = XClass()
print 'x(1, 2)', x(1, 2)
print 'callable(x)', callable(x)  # True
print 'add(1, 2)', add(1, 2)
print 'callable(add)', callable(add)  # True

Python3中的差异

  • Python3中,str与unicode的区别被废除了,因而__unicode__没有了,取而代之地出现了__bytes__.

  • Python3中,pision默认就是true pision, 因而__p__废弃.

  • __coerce__因存在冗余而废弃.

  • __cmp__因存在冗余而废弃.

  • __nonzero__改名为__bool__.

위 내용은 Python의 매직 메소드 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.