類型轉換魔法
類型轉換魔法其實就是實現了str、int等工廠函數的結果,通常這些函數還有類型轉換的功能,下面是一些相關的魔法方法:
-
__int__(self)
-
轉換成整型,對應int函數。
-
__long__(self)
-
轉換成長整型,對應long函數。
-
__float__(self)
-
轉換成浮點型,對應float函數。
-
__complex__(self)
-
轉換成 復數型,對應complex函數。
-
__oct__(self)
-
轉換成八進制,對應oct函數。
-
__hex__(self)
-
轉換成十六進制,對應hex函數。
-
__index__(self)
-
首先,這個方法應該返回一個整數,可以是int或者long。這個方法在兩個地方有效,首先是 operator 模塊中的index函數得到的值就是這個方法的返回值,其次是用於切片操作,下面會專門進行代碼演示。
-
__trunc__(self)
-
當 math.trunc(self) 使用時被調用.__trunc__返回自身類型的整型截取 (通常是一個長整型).
-
__coerce__(self, other)
-
實現了類型的強制轉換,這個方法對應於 coerce 內建函數的結果(python3.0開始去掉了此函數,也就是該魔法方法也沒意義了,至於后續的版本是否重新加入支持,要視官方而定。)
- 這個函數的作用是強制性地將兩個不同的數字類型轉換成為同一個類型,例如:
#coerce(x, y) -> (x1, y1)
方法返回一個元祖,分別對應轉換后的兩個數字。其優先級為:復數>浮點數>長整型>整型。在轉換的時候,會轉換為兩個參數中優先級高的類型。當轉換無法完成的時候,會觸發 TypeError。
而當我們定義這個魔法方法時,如果轉換無法完成,應該返回None。
這里有個重要的機制,當python進行運算的時候,如 1 + 1.0 時,會先調用 coerce 函數將其轉換為同一個類型,然后再進行運行,這也就是為什么 1 + 1.0 = 2.0,因為轉換之后實際進行的運算為 1.0 +1.0。得到這樣的結果也就不奇怪了。
代碼示例:
class Foo(object): def __init__(self, x): self.x = x def __int__(self): return int(self.x) + 1 def __long__(self): return long(self.x) + 1 a = Foo(123) print int(a) print long(a) print type(int(a)) print type(long(a))
這里要注意一點,魔法方法的返回值必須符合預期,例如 __int__ 就應該返回一個 int 類型,如果我們任性地返回其他類型,例如字符串(str)、列表(list)等,會報錯。
def __int__(self): return str(self.x)
def __int__(self): return list(self.x)
但是 int 可以返回 long,而 long 返回 int 時會自動被處理成 long:
class Foo(object): def __init__(self, x): self.x = x def __int__(self): return long(self.x) + 1 def __long__(self): return int(self.x) + 1 a = Foo(123) print int(a) print long(a) print type(int(a)) print type(long(a))
以上發生在python2.7.11上,這是一個很奇怪的行為,以至於我認為其可能是一個 BUG,總之我們在使用的時候要注意要返回對應的類型就是了,以免出錯。
__index__(self):
首先是對應於operator.index(),operator.index(a)就相當於a.__index__():
import operator class Foo(object): def __init__(self, x): self.x = x def __index__(self): return self.x + 1 a = Foo(10) print operator.index(a)
另一個是很神奇的特效,當其用於序列中時:
class Foo(object): def __init__(self, x): self.x = x def __index__(self): return 3 a = Foo('scolia') b = [1, 2, 3, 4, 5] print b[a] print b[3]
可以作為索引一樣使用,可進行切片操作:
class Foo(object): def __init__(self, x): self.x = x def __index__(self): return int(self.x) a = Foo('1') b = Foo('3') c = [1, 2, 3, 4, 5] print c[a:b]
其實切片內部使用的函數 slice 對其進行了處理,有興趣的同學可以去了解這個函數:
a = Foo('1') b = Foo('3') c = slice(a, b) print c d = [1, 2, 3, 4, 5] print d[c]
__coerce__(self, other):
代碼示例:
class Foo(object): def __init__(self, x): self.x = x def __coerce__(self, other): return self.x, str(other.x) class Boo(object): def __init__(self, x): self.x = x def __coerce__(self, other): return self.x, int(other.x) a = Foo('123') b = Boo(123) print coerce(a, b) print coerce(b, a)
總結:是調用了第一個參數的魔法方法。
類的表示 :
類的表示其實就是對外的特征,例如使用print語句時,打印出來的是什么,其實本質上也是對應函數的輸出:
-
__str__(self)
-
定義當 str() 被你的一個類的實例調用時所要產生的行為。因為print默認調用的就是str()函數。
-
__repr__(self)
-
定義當 repr() 被你的一個類的實例調用時所要產生的行為。 str() 和 repr() 的主要區別是其目標群體。 repr() 返回的是機器可讀的輸出,而 str() 返回的是人類可讀的。 repr() 函數是交換模式默認調用的
-
函數。
-
__unicode__(self)
-
定義當 unicode() 被你的一個類的實例調用時所要產生的行為。 unicode() 和 str() 很相似,但是返回的是unicode字符串。注意,如果對你的類調用 str() 然而你只定義了 __unicode__() ,那么其將不會
-
工作。你應該定義 __str__() 來確保調用時能返回正確的值,並不是每個人都有心情去使用unicode()。
-
__format__(self, formatstr)
-
定義當你的一個類的實例被用來用新式的格式化字符串方法進行格式化時所要產生的行為。例如, "Hello, {0:abc}!".format(a) 將會導致調用 a.__format__("abc") 。這對定義你自己的數值或字符串類型
-
是十分有意義的,你可能會給出一些特殊的格式化選項。
-
__hash__(self)
-
定義當 hash()被你的一個類的實例調用時所要產生的行為。它必須返回一個整數,用來在字典中進行快速比較。
-
請注意,實現__hash__時通常也要實現__eq__。有下面這樣的規則:a == b 暗示着 hash(a) == hash(b) 。也就是說兩個魔法方法的返回值最好一致。
- 這里引入一個‘可哈希對象’的概念,首先一個可哈希對象的哈希值在其生命周期內應該是不變的,而要得到哈希值就意味要實現__hash__方法。而哈希對象之間是可以比較的,這意味着要實現__eq__或
- 者__cmp__方法,而哈希對象相等必須其哈希值相等,要實現這個特性就意味着__eq__的返回值必須和__hash__一樣。
- 可哈希對象可以作為字典的鍵和集合的成員,因為這些數據結構內部使用的就是哈希值。python中所有內置的不變的對象都是可哈希的,例如元組、字符串、數字等;而可變對象則不能哈希,例如列表、
- 字典等。
- 用戶定義的類的實例默認是可哈希的,且除了它們本身以外誰也不相等,因為其哈希值來自於 id 函數。但這並不代表 hash(a) == id(a),要注意這個特性。
-
__nonzero__(self)
-
定義當 bool() 被你的一個類的實例調用時所要產生的行為。本方法應該返回True或者False,取決於你想讓它返回的值。(python3.x中改為__bool__)
-
__dir__(self)
-
定義當 dir() 被你的一個類的實例調用時所要產生的行為。該方法應該返回一個屬性的列表給用戶。
-
__sizeof__(self)
-
定義當 sys.getsizeof() 被你的一個類的實例調用時所要產生的行為。該方法應該以字節為單位,返回你的對象的大小。這通常對於以C擴展的形式實現的Python類更加有意義,其有助於理解這些擴展。
這里並沒有什么特別難以理解的地方,所以代碼例子就略去了。
歡迎大家交流。