摘要:的魔術(shù)方法是中那些預(yù)定義的像類型的函數(shù)。使用的魔術(shù)方法的最大優(yōu)勢在于提供了簡單的方法讓對象可以表現(xiàn)得像內(nèi)置類型一樣。廖雪峰老師教程里寫的是方法,不知道為啥。
Python的魔術(shù)方法是Python中那些預(yù)定義的像__XXX__類型的函數(shù)。
使用Python的魔術(shù)方法的最大優(yōu)勢在于python提供了簡單的方法讓對象可以表現(xiàn)得像內(nèi)置類型一樣。
__str__函數(shù)用于處理打印實例本身的時候的輸出內(nèi)容。如果沒有覆寫該函數(shù),則默認(rèn)輸出一個對象名稱和內(nèi)存地址。
例如:
>>> class Student(object): ... def __init__(self,name): ... self._name = name ... >>> print Student()
輸出:<__main__.Student object at 0x0000000002A929E8>.
那么我們?nèi)绾巫屳敵龅慕Y(jié)果可讀性更高一點呢?我們可以覆寫__str__函數(shù)。例如
>>> class Student(object): ... def __init__(self, name): ... self._name = name ... def __str__(self): ... return "I"m a student, named %s" % self._name ... >>> print Student("Charlie")
輸出結(jié)果就是:I"m a student, named Charlie.
我們將str()函數(shù)作用于該對象的時候,其實是調(diào)用了該對象的__str__函數(shù)。
__repr__也是將對象序列化,但是__repr__更多的是給python編譯器看的。__str__更多的是可讀性(readable)。
我們將repr()函數(shù)作用于摸某一個對象的時候,調(diào)用的其實就是該函數(shù)的__repr__函數(shù)。
與repr()成對的是eval()函數(shù)。eval()函數(shù)是將序列化后的對象重新轉(zhuǎn)為對象。前提是該對象實現(xiàn)了__repr__函數(shù)。
上面這一段話基于自己的理解,不知道對錯。
>>> item = [1,2,3] >>> repr(item) "[1, 2, 3]" >>> other_item = eval(repr(item)) >>> other_item[1] 2__iter__函數(shù)
我們經(jīng)常對list或者tuple使用for...in...來迭代。那是list繼承自Iterable。Iterable實現(xiàn)了__iter__函數(shù)。
要想將一個自定義的對象變成一個可迭代的對象,那么必須要實現(xiàn)兩個方法:__iter__和next.
__iter__函數(shù)返回一個對象。迭代的時候則會不斷地調(diào)用next函數(shù)拿到下一個值,直到捕獲到StopIteration停止。
廖雪峰老師教程里寫的是__next__方法,不知道為啥。
class Fib(object): def __init__(self): self.a, self.b = 0, 1 def __iter__(self): return self def next(self): self.a, self.b = self.b, self.a + self.b if self.a > 10000: raise StopIteration return self.a for i in Fib(): print i__getitem__函數(shù)
上面通過實現(xiàn)__iter__函數(shù)實現(xiàn)對象的迭代。
那么如何實現(xiàn)對象按下標(biāo)取出元素呢。
這是通過實現(xiàn)對象的__getitem__方法。
我們來舉一個?子。我們新建了一個類MyList,我們要辦它實現(xiàn)普通list的一些功能,比如(1)根據(jù)下標(biāo)獲取值;(2)正數(shù)順序單步長切片 (3)任意步長切片
class MyList(object): def __init__(self, *args): self.numbers = args def __getitem__(self, item): return self.numbers[item] my_list = MyList(1, 2, 3, 4, 6, 5, 3) print my_list[2]
當(dāng)然,上面實現(xiàn)了根據(jù)下標(biāo)獲取值。但是這還不夠。我們還需要實現(xiàn)切片功能。例如my_list[1:3].
我們對對象進(jìn)行切片操作的時候,調(diào)用的氣勢也是__getitem__函數(shù)。此時,該函數(shù)獲取到的并不是int對象,而是slice對象。
例如下面的代碼
class MyList(object): def __init__(self, *args): self.numbers = args def __getitem__(self, item): if isinstance(item, int): return self.numbers[item] elif isinstance(item, slice): # 寫習(xí)慣了其他語言,差點忘記了三元運算符的格式了,吼吼吼。 # 下面句三元運算符的意思是,若為空,則為切片從0開始。 start = item.start if item.start is not None else 0 # 下面句三元運算符的意思是,若為空,則為切片到最末端結(jié)束。 stop = item.stop if item.stop is not None else len(self.numbers) return self.numbers[start:stop] my_list = MyList(1, 2, 3, 4, 6, 5, 3) print my_list[2:5]
上面的代碼終于實現(xiàn)了切片功能,但是還沒考慮負(fù)數(shù)呢。那么我們加一把勁再來改一下。代碼如下:
class MyList(object): def __init__(self, *args): self.numbers = args def __getitem__(self, item): if isinstance(item, int): return self.numbers[item] elif isinstance(item, slice): start = item.start if item.start is not None else 0 stop = item.stop if item.stop is not None else len(self.numbers) length = len(self.numbers) start = length + start + 1 if start < 0 else start stop = length + stop + 1 if stop < 0 else stop return self.numbers[start:stop] my_list = MyList(1, 2, 3, 4, 6, 5, 3) print my_list[1:-1]
哇塞,寫完了,棒棒棒
_getattar_在調(diào)用某一個對象不存在的屬性或者方法的時候,會拋出一個一個AttributeError錯誤。
但是如果我們實現(xiàn)了類中的魔術(shù)方法__getattar__,那么在調(diào)用不存在的屬性或者方法的時候,就會調(diào)用該魔術(shù)方法。
class Apple(object): def __getattr__(self, item): if item == "attar1": return "print" if item == "method1": return lambda x: "hello %s" % x apple = Apple() print apple.attar1 print apple.method1
__getattar__函數(shù)一個重要的適用場景就是實現(xiàn)鏈?zhǔn)秸{(diào)用。例如我們在調(diào)用某一個api的時候:
GET users/articles/index
那么我們就希望我們的代碼可以實現(xiàn)`Api.users.articles.index這么調(diào)用。
思考一下,要實現(xiàn)鏈?zhǔn)秸{(diào)用,最重要的就是每一個調(diào)用都是返回一個實例~~。
# coding=utf-8 class Api(object): def __init__(self, path=""): self._path = path def __getattr__(self, name): return Api("%s/%s" % (self._path, name)) # 定義一個Post方法來發(fā)送請求 def post(self): print self._path api = Api() api.user.articles.index.post()
廖雪峰在他的教程中給我們出了一個題目:
例如調(diào)用github的api:users/:user/repos一樣,中間的user名需要動態(tài)替換。
我們希望能api.users("charlie").repos這么調(diào)用。那么代碼該如何實現(xiàn)呢?這可能需要用到另一個方法__call__
一個對象既有屬性,又有方法。我們在調(diào)用一個實例的方法的時候,我們可以使用instance.method()的形式調(diào)用。
其實也可以將實例本身看成一個函數(shù)用來調(diào)用,我們需要做的就是實現(xiàn)__call__函數(shù)本身。
class Apple(object): def __call__(self, *args, **kwargs): return args apple = Apple() print apple("yes", "no")
此時我們再來看一下上面提到的實現(xiàn)api.users("charlie").repos鏈?zhǔn)秸{(diào)用的方法。
# coding=utf-8 class Api(object): def __init__(self, path=""): self._path = path def __getattr__(self, name): return Api("%s/%s" % (self._path, name)) def __call__(self, args): self._path = "%s/%s" % (self._path, args) return Api(self._path) # 定義一個Post方法來發(fā)送請求 def post(self): print self._path api = Api() api.users("Charlie").index.post()
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/38334.html
摘要:由此看來,的官方文檔就把當(dāng)成內(nèi)置函數(shù),這個認(rèn)識錯誤是有根源的等到的時候,官方把錯誤改正過來了,然而改得并不徹底。使用進(jìn)行判斷,結(jié)果為的才是內(nèi)置函數(shù)。 showImg(https://segmentfault.com/img/bVbm3Bu?w=5184&h=3456);有群友問過,是什么原因使我開始寫技術(shù)公眾號,又是什么動力讓我堅持寫的。 在我看來,寫作是一件不能敷衍的事,通過寫作來學(xué)...
摘要:本篇繼續(xù)學(xué)習(xí)之路,實現(xiàn)更多的特殊方法以讓自定義類的行為跟真正的對象一樣。之所以要讓向量不可變,是因為我們在計算向量的哈希值時需要用到和的哈希值,如果這兩個值可變,那向量的哈希值就能隨時變化,這將不是一個可散列的對象。 《流暢的Python》筆記。本篇是面向?qū)ο髴T用方法的第二篇。前一篇講的是內(nèi)置對象的結(jié)構(gòu)和行為,本篇則是自定義對象。本篇繼續(xù)Python學(xué)習(xí)之路20,實現(xiàn)更多的特殊方法以讓...
摘要:它是語言的第七種數(shù)據(jù)類型前六種是布爾值字符串?dāng)?shù)值對象。為了防止沖突這就是引入的原因。指向了這個內(nèi)部方法調(diào)用了返回對象的屬性等于一個布爾值,表示該對象使用時,是否可以展開。數(shù)組的默認(rèn)行為是可以展開返回對象的屬性,指向當(dāng)前對象的構(gòu)造函數(shù)。 es6學(xué)習(xí)筆記-Symbol_v1.0 基本抄了一次內(nèi)容,有很多只是知道其然并不知其所以然,不過也算是加深了一次印象,另外每段代碼我都有手動執(zhí)行過. E...
閱讀 2186·2021-11-25 09:43
閱讀 2274·2021-11-24 09:39
閱讀 1588·2021-11-22 12:02
閱讀 3001·2021-11-17 09:33
閱讀 3425·2021-11-15 11:38
閱讀 2780·2021-10-13 09:40
閱讀 1084·2021-09-22 15:41
閱讀 1697·2019-08-30 10:58