在Python中,所有以」__」雙底線包起來的方法,都統稱為」魔術方法」。例如我們接觸最多的__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__
。
__new__
方法總是需要傳回該類別的一個實例,而__init__
不能傳回除了None的任何值。例如下面範例:
class Foo(object): def __init__(self): print 'foo __init__' return None # 必须返回None,否则抛TypeError def __del__(self): print 'foo __del__'
實際中,你很少會用到__new__
,除非你希望能夠控制類別的建立。
如果要講解__new__
,往往需要牽扯到metaclass
(元類別)的介紹。
如果你有興趣深入,可以參考我的另一篇部落格: 理解Python的metaclass
對於__new__
的重載,Python文件中也有了詳細的介紹。
在物件的生命週期結束時, __del__
會被呼叫,可以將__del__
理解為」構析函數」.__del__
定義的是當一個物件進行垃圾回收時的行為。
有一點容易被人誤解, 實際上,x.__del__()
並不是對於del x
#的實現,但是往往執行del x
時會呼叫x.__del__()
.
怎麼來理解這句話呢? 繼續用上面的Foo類的程式碼為例:
foo = Foo() foo.__del__() print foo del foo print foo # NameError, foo is not defined
如果呼叫了foo.__del__()
,物件本身仍然存在. 但是調用了del foo
, 就再也沒有foo這個物件了.
請注意,如果解釋器退出的時候物件還存在,就不能保證 __del__
被確切的執行了。所以__del__
並不能取代良好的程式設計習慣。
例如,在處理socket時,及時關閉結束的連線。
總是有人要吐槽Python缺少對於類別的封裝,例如希望Python能夠定義私有屬性,然後提供公共可存取的getter和 setter。 Python其實可以透過魔術方法來實現封裝。
__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__
同樣要避免」無限遞迴」的錯誤。
要提醒的是,最好不要嘗試去實作__getattribute__
,因為很少見到這種做法,而且很容易出bug。
範例說明__setattr__
的無限遞迴錯誤:
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__
在其拥有者对其进行删除的时候调用。
在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[&#39;a&#39;] # raise TypeError, 该异常没有被__exit__处理 输出: <type &#39;exceptions.IndexError&#39;> <type &#39;exceptions.TypeError&#39;> 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 &#39;distance __init__&#39; self.meter = meter data = { &#39;foo&#39;: [1, 2, 3], &#39;bar&#39;: (&#39;Hello&#39;, &#39;world!&#39;), &#39;baz&#39;: True, &#39;dt&#39;: datetime(2016, 10, 01), &#39;distance&#39;: Distance(1.78), } print &#39;before dump:&#39;, data with open(&#39;data.pkl&#39;, &#39;wb&#39;) as jar: pickle.dump(data, jar) # 将数据存储在文件中 del data print &#39;data is deleted!&#39; with open(&#39;data.pkl&#39;, &#39;rb&#39;) as jar: data = pickle.load(jar) # 从文件中恢复数据 print &#39;after load:&#39;, 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: &#39;&#39;&#39;Class to store a string and a changelog, and forget its value when pickled.&#39;&#39;&#39; 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 &#39;Changelog for Slate object:&#39; for k, v in self.history: print &#39;%s %s&#39; % (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(&#39;slate.pkl&#39;, &#39;wb&#39;) as jar: pickle.dump(slate, jar) del slate # delete it with open(&#39;slate.pkl&#39;, &#39;rb&#39;) as jar: slate = pickle.load(jar) print &#39;current value:&#39;, 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_ = [&#39;a&#39;, &#39;b&#39;, &#39;c&#39;] print list_[thing] # &#39;b&#39; 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: &#39;apple&#39;, 2: &#39;banana&#39;, 3: &#39;cat&#39;} print dict_[thing] # raise KeyError
这个时候就不是调用__index__
了。虽然list
和dict
都实现了__getitem__
方法, 但是它们的实现方式是不一样的。
如果希望上面例子能够正常执行, 需要实现Thing的__hash__
和 __eq__
方法.
class Thing(object): def __hash__(self): return 1 def __eq__(self, other): return hash(self) == hash(other) dict_ = {1: &#39;apple&#39;, 2: &#39;banana&#39;, 3: &#39;cat&#39;} 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 &#39;StrDemo2&#39; class StrDemo3: def __unicode__(self): return u&#39;StrDemo3&#39; 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 &#39;x(1, 2)&#39;, x(1, 2) print &#39;callable(x)&#39;, callable(x) # True print &#39;add(1, 2)&#39;, add(1, 2) print &#39;callable(add)&#39;, callable(add) # True
Python3中,str与unicode的区别被废除了,因而__unicode__
没有了,取而代之地出现了__bytes__
.
Python3中,pision默认就是true pision, 因而__p__
废弃.
__coerce__
因存在冗余而废弃.
__cmp__
因存在冗余而废弃.
__nonzero__
改名为__bool__
.
以上是Python中的魔術方法介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!