日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
Python程序員都該用的一個(gè)庫(kù)

本文的作者來(lái)自知名 Python 庫(kù) Twisted 開發(fā)團(tuán)隊(duì),首先舉例說明了在 Python 中定義類是多么的麻煩,然后給出了自己的解決方案:attrs 庫(kù)。從介紹來(lái)看,確實(shí)方便很多。

創(chuàng)新互聯(lián)公司是創(chuàng)新、創(chuàng)意、研發(fā)型一體的綜合型網(wǎng)站建設(shè)公司,自成立以來(lái)公司不斷探索創(chuàng)新,始終堅(jiān)持為客戶提供滿意周到的服務(wù),在本地打下了良好的口碑,在過去的十年時(shí)間我們累計(jì)服務(wù)了上千家以及全國(guó)政企客戶,如成都輕質(zhì)隔墻板等企業(yè)單位,完善的項(xiàng)目管理流程,嚴(yán)格把控項(xiàng)目進(jìn)度與質(zhì)量監(jiān)控加上過硬的技術(shù)實(shí)力獲得客戶的一致稱贊。

你寫 Python 程序嗎?那你應(yīng)該使用 attrs。

你問為什么?我只能說,不要問,直接用就好了。

好吧,我還是解釋一下。

我熱愛 Python,這十多年來(lái)一直是我的主力編程語(yǔ)言。盡管期間也出現(xiàn)過一些有意思的語(yǔ)言(指的是 Haskell 和 Rust),但我還不打算換到其他語(yǔ)言。

這不是說 Python 沒有本身沒有任何問題。在某些情況下,Python 會(huì)讓你更容易犯錯(cuò)。尤其是一些庫(kù)大量使用類繼承,以及 God-object 反面模式。

導(dǎo)致該情況的一個(gè)原因可能是 Python 是一種非常方便的語(yǔ)言,所以經(jīng)驗(yàn)欠缺的程序員犯錯(cuò)誤后,他們就得繼續(xù)忍受下去。

但我想,更重要的原因也許是,有時(shí)你努力做正確的事,但 Python 卻會(huì)因此懲罰你。

在對(duì)象設(shè)計(jì)的大背景下,“正確的事“是指設(shè)計(jì)體量小并且獨(dú)立的類,只做一件事,并且把這件事做好。例如,如果你的對(duì)象開始累積大量的私有方法,也許你應(yīng)該將它們變成私有屬性的公有方法。但是,這種事處理起來(lái)非常乏味,你可能就不會(huì)理會(huì)這些。

如果你有一些相關(guān)的數(shù)據(jù),而且數(shù)據(jù)之間的關(guān)系和行為是需要進(jìn)行解釋的,那么應(yīng)該定義為對(duì)象。在 Python 中定義元組和列表非常方便。剛開始把 address = ... 寫成 host, port = ... ,可能覺得沒什么關(guān)系,但很快你就會(huì)到處寫 [(family, socktype, proto, canonname, sockaddr)] = ... 這樣的語(yǔ)句,這時(shí)就該后悔了。這還是算你走運(yùn)的情況。如果倒霉的話,你可能得維護(hù) values[0][7][4][HOSTNAME][“canonical”] 這樣的代碼,這時(shí)你的心情是痛苦,而不僅僅是后悔了。


這就提出了一個(gè)問題:在 Python 中使用類是否是麻煩?我們來(lái)看一個(gè)簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu):一個(gè)三維直角坐標(biāo)。從最簡(jiǎn)單的開始:

  
 
  1. class Point3D(object): 

到現(xiàn)在為止還挺好。我們已經(jīng)有了一個(gè)三維點(diǎn)。 接下來(lái)呢?

  
 
  1. class Point3D(object): 
  2.     def __init__(self, x, y, z): 

其實(shí),這是有點(diǎn)可惜。我只想對(duì)數(shù)據(jù)的打包,但卻不得不覆蓋一個(gè) Python 運(yùn)行時(shí)中的特殊方法,而且命名還是約定俗成的。但還不算太壞;畢竟所有的編程語(yǔ)言都是按照某種形式組成的怪異符號(hào)而已。

至少可以看到屬性名了,還能說得通。

  
 
  1. class Point3D(object): 
  2.     def __init__(self, x, y, z): 
  3.         self.x 

我已經(jīng)說過,我想一個(gè) x,但現(xiàn)在必須把它指定為一個(gè)屬性...

  
 
  1. class Point3D(object): 
  2.     def __init__(self, x, y, z): 
  3.         self.x = x 

綁定到 x ?呃,很明顯...

  
 
  1. class Point3D(object): 
  2.     def __init__(self, x, y, z): 
  3.         self.x = x 
  4.         self.y = y 
  5.         self.z = z 

每個(gè)屬性都得這么做一次,所以這相當(dāng)糟糕?每個(gè)屬性名都得敲 3 次????

好吧。至少定義完了。

  
 
  1. class Point3D(object): 
  2.     def __init__(self, x, y, z): 
  3.         self.x = x 
  4.         self.y = y 
  5.         self.z = z 
  6.     def __repr__(self): 

什么,難道還沒結(jié)束嗎?

  
 
  1. class Point3D(object): 
  2.     def __init__(self, x, y, z): 
  3.         self.x = x 
  4.         self.y = y 
  5.         self.z = z 
  6.     def __repr__(self): 
  7.         return (self.__class__.__name__ + 
  8.                 ("(x={}, y={}, z={})".format(self.x, self.y, self.z))) 

拜托?,F(xiàn)在我得每個(gè)屬性名敲 5 次了,如果我想在調(diào)試時(shí)知道屬性到底指的是什么的話。如果定義元組的話,就不用這一步了???????

  
 
  1. class Point3D(object): 
  2.     def __init__(self, x, y, z): 
  3.         self.x = x 
  4.         self.y = y 
  5.         self.z = z 
  6.     def __repr__(self): 
  7.         return (self.__class__.__name__ + 
  8.                 ("(x={}, y={}, z={})".format(self.x, self.y, self.z))) 
  9.     def __eq__(self, other): 
  10.         if not isinstance(other, self.__class__): 
  11.             return NotImplemented 
  12.         return (self.x, self.y, self.z) == (other.x, other.y, other.z) 

敲 7 次???????!?

  
 
  1. class Point3D(object): 
  2.     def __init__(self, x, y, z): 
  3.         self.x = x 
  4.         self.y = y 
  5.         self.z = z 
  6.     def __repr__(self): 
  7.         return (self.__class__.__name__ + 
  8.                 ("(x={}, y={}, z={})".format(self.x, self.y, self.z))) 
  9.     def __eq__(self, other): 
  10.         if not isinstance(other, self.__class__): 
  11.             return NotImplemented 
  12.         return (self.x, self.y, self.z) == (other.x, other.y, other.z) 
  13.     def __lt__(self, other): 
  14.         if not isinstance(other, self.__class__): 
  15.             return NotImplemented 
  16.         return (self.x, self.y, self.z) < (other.x, other.y, other.z) 

敲 9 次?!???????!?

  
 
  1. from functools import total_ordering 
  2. @total_ordering 
  3. class Point3D(object): 
  4.     def __init__(self, x, y, z): 
  5.         self.x = x 
  6.         self.y = y 
  7.         self.z = z 
  8.     def __repr__(self): 
  9.         return (self.__class__.__name__ + 
  10.                 ("(x={}, y={}, z={})".format(self.x, self.y, self.z))) 
  11.     def __eq__(self, other): 
  12.         if not isinstance(other, self.__class__): 
  13.             return NotImplemented 
  14.         return (self.x, self.y, self.z) == (other.x, other.y, other.z) 
  15.     def __lt__(self, other): 
  16.         if not isinstance(other, self.__class__): 
  17.             return NotImplemented 
  18.         return (self.x, self.y, self.z) < (other.x, other.y, other.z) 

好了,擦汗 - 盡管多了 2 行代碼不是很好,但至少現(xiàn)在我們不用定義其他比較方法了?,F(xiàn)在一切搞定了,對(duì)吧?

  
 
  1. from unittest import TestCase 
  2. class Point3DTests(TestCase): 

你知道嗎? 我受夠了。一個(gè)類碼了 20 行,卻還什么事都沒做;我們這樣做是想解四元方程,而不是定義“可以打印和比較的數(shù)據(jù)結(jié)構(gòu)”。我陷入了大量無(wú)用的垃圾元組、列表和字典中;用 Python 定義合適的數(shù)據(jù)結(jié)構(gòu)是非常麻煩的

命名元組 namedtuple

為解決這個(gè)難題,標(biāo)準(zhǔn)庫(kù)給出的解決方案是使用 namedtuple 。然而不幸的是初稿(在許多方面與我自己的處理方式有相似的尷尬的和過時(shí)之處)namedtuple 仍然無(wú)法挽救這個(gè)現(xiàn)象。它引入了大量沒有必要的公共函數(shù),這對(duì)于兼容性維護(hù)來(lái)說簡(jiǎn)直就是一場(chǎng)噩夢(mèng),并且它連問題的一半都沒有解決。這種做法的缺陷太多了,這里只列一些重點(diǎn):

  • 不管你是否希望如此,它的字段都可以通過數(shù)字索引的方式訪問。這意味你不能有私有屬性,因?yàn)樗袑傩酝ㄟ^公開的 __getitem__ 接口暴露出來(lái)。

  • 它等同于有相同值的原始元組,因此很容易發(fā)生類型混亂,特別是如果你想避免使用元組和列表。

  • 這是一個(gè)元組,所以它總是不可變的。

至于***一點(diǎn),你可以像這樣使用:

  
 
  1. Point3D = namedtuple('Point3D', ['x', 'y', 'z']) 

在這種情況下它看起來(lái)并不像一種類;無(wú)特殊情況下,簡(jiǎn)單的語(yǔ)法分析工具將不能識(shí)別它為類。但是這樣你不能給它添加任何其他方法,因?yàn)闆]有地方放任何的方法。更別提你必須輸入類的名字兩次。

或者你可以使用繼承:

  
 
  1. class Point3D(namedtuple('_Point3DBase', 'x y z'.split())): 
  2.     pass 

盡管這樣可以添加方法和文檔字符串,看起來(lái)也像一個(gè)類,但是內(nèi)部名稱(在 repr 中顯示的內(nèi)容,并不是類的真實(shí)名稱)變的很怪了。同時(shí),你還不知不覺中把沒列出的屬性變成了可變的,這是添加 class 聲明的一個(gè)奇怪的副作用;除非你在類主體中添加 __slots__='X Y z'.split(),但這樣又回到了每個(gè)屬性名必須敲兩次的情況。

而且,我們還沒提科學(xué)已經(jīng)證明不應(yīng)該使用繼承呢。

因此,如果你只能選命名元組,那就選命名元組吧,也算是改進(jìn),雖然只是在部分情況下如此。

使用 attrs

這時(shí)該我最喜歡的 Python 庫(kù)出場(chǎng)了。

pip install attrs

我們重新審視一下上述問題。如何使用 attrs 庫(kù)編寫 Point3D ?

  
 
  1. import attr 
  2. @attr.s 

由于它還沒有內(nèi)置到 Python 中,所以必須用以上 2 行開始:導(dǎo)入包然后使用類裝飾器。

  
 
  1. import attr 
  2. @attr.s 
  3. class Point3D(object): 

你看,沒有繼承!通過使用類裝飾器,Point3D 仍然是一個(gè)普通的 Python 類(盡管我們一會(huì)會(huì)看到一些雙下劃線方法)。

  
 
  1. import attr 
  2. @attr.s 
  3. class Point3D(object): 
  4.     x = attr.ib() 

添加屬性 x。

  
 
  1. import attr 
  2. @attr.s 
  3. class Point3D(object): 
  4.     x = attr.ib() 
  5.     y = attr.ib() 
  6.     z = attr.ib() 

再分別添加屬性 yz。這樣就完成了。

這就 OK 了? 等等。不用定義字符串表示嗎?

  
 
  1. >>> Point3D(1, 2, 3) 
  2. Point3D(x=1, y=2, z=3) 

怎么進(jìn)行比較?

  
 
  1. >>> Point3D(1, 2, 3) == Point3D(1, 2, 3) 
  2. True 
  3. >>> Point3D(3, 2, 1) == Point3D(1, 2, 3) 
  4. False 
  5. >>> Point3D(3, 2, 3) > Point3D(1, 2, 3) 
  6. True 

好的。但如果我想將有明確屬性定義的數(shù)據(jù)提取為適合 JSON 序列化的格式呢?

  
 
  1. >>> attr.asdict(Point3D(1, 2, 3)) 
  2. {'y': 2, 'x': 1, 'z': 3} 

也許上邊有一點(diǎn)點(diǎn)準(zhǔn)確。即使如此,因?yàn)槭褂昧?attrs 后,很多事情都變得更簡(jiǎn)單了,它允許你在類上聲明字段,以及相關(guān)的元數(shù)據(jù)。

  
 
  1. pprint 
  2. >>> pprint.pprint(attr.fields(Point3D)) 
  3. (Attribute(name='x', default=NOTHING, validator=None, repr=True, cmp=True, hash=True, init=True, convert=None), 
  4.  Attribute(name='y', default=NOTHING, validator=None, repr=True, cmp=True, hash=True, init=True, convert=None), 
  5.  Attribute(name='z', default=NOTHING, validator=None, repr=True, cmp=True, hash=True, init=True, convert=None)) 

我不打算在這里深入介紹 attrs 的每一個(gè)有趣的功能;你可以閱讀它的文檔。另外,項(xiàng)目會(huì)經(jīng)常更新,每隔一段時(shí)間都會(huì)有新的東西出現(xiàn),因此我也可能會(huì)漏掉一些重要的功能。但是用上 attrs 之后 ,你會(huì)發(fā)現(xiàn)它所做的正式此前 Python 所缺乏的:

  1. 它讓你簡(jiǎn)潔地定義類型,而不是通過手動(dòng)鍵入 def __init __ 的方式來(lái)定義。

  2. 它讓你直接地說出你聲明的意思,而不是拐彎抹角的表達(dá)它。與其這樣說:“我有一個(gè)類型,它被稱為 MyType ,它有一個(gè)構(gòu)造函數(shù),在構(gòu)造函數(shù)中用參數(shù) 'A' 給屬性 'A' 賦值”,而是應(yīng)該這樣說:“我有一個(gè)類型,它被稱為 MyType ,它有一個(gè)屬性叫做 a,以及跟它相關(guān)的方法“,而不必通過逆向工程猜測(cè)它的方法(例如,在一個(gè)實(shí)例中運(yùn)行 dir ,或查看 self.__ class__. __dict__)。

  3. 它提供了有用的默認(rèn)方法,而不像 Python 中的默認(rèn)行為有時(shí)有用,大部分時(shí)候沒用。

  4. 它從簡(jiǎn)單的開始,但是提供了后續(xù)添加更嚴(yán)謹(jǐn)實(shí)現(xiàn)的空間。

我們?cè)敿?xì)說明***一點(diǎn)。

逐步改善

雖然我不打算談及每一個(gè)功能,但如果我沒有提到以下幾個(gè)特點(diǎn),那我就太不負(fù)責(zé)任了。你可以從上面這些特別長(zhǎng)的 Attributerepr() 中看到一些有趣的東西。

例如:你通過用 @attr.s 修飾類來(lái)驗(yàn)證屬性。比如:Point3D 這個(gè)類,應(yīng)該包含數(shù)字。為簡(jiǎn)單起見,我們可以說這些數(shù)字為 float 類型,像這樣:

  
 
  1. import attr 
  2. from attr.validators import instance_of 
  3. @attr.s 
  4. class Point3D(object): 
  5.     x = attr.ib(validator=instance_of(float)) 
  6.     y = attr.ib(validator=instance_of(float)) 
  7.     z = attr.ib(validator=instance_of(float)) 

因?yàn)槲覀兪褂昧?attrs ,這意味著之后有機(jī)會(huì)進(jìn)行驗(yàn)證:可以只給每個(gè)需要的屬性添加類型信息。其中的一些功能,可以讓我們避免常見的錯(cuò)誤。例如,這是一個(gè)很常見的“找 Bug” 面試題:

  
 
  1. class Bag: 
  2.     def __init__(self, contents=[]): 
  3.         self._contents = contents 
  4.     def add(self, something): 
  5.         self._contents.append(something) 
  6.     def get(self): 
  7.         return self._contents[:] 

修正它,正確的代碼應(yīng)該是這個(gè)樣子:

  
 
  1. class Bag: 
  2.     def __init__(self, contents=None): 
  3.         if contents is None: 
  4.             contents = [] 
  5.         self._contents = contents 

額外添加了 2 行代碼。

這樣,contents 無(wú)意間就成了全局變量,這使得所有沒有提供列表的 Bag 對(duì)象都共享一個(gè)列表。使用 attrs 的話,就變成這樣:

  
 
  1. @attr.s 
  2. class Bag: 
  3.     _contents = attr.ib(default=attr.Factory(list)) 
  4.     def add(self, something): 
  5.         self._contents.append(something) 
  6.     def get(self): 
  7.         return self._contents[:] 

attrs 還提供一些其他的特性,讓你在構(gòu)建類時(shí)更方便更正確。另一個(gè)很好的例子?如果你嚴(yán)格的管控對(duì)象的屬性(或在內(nèi)存使用上更有效率的 CPython ),你可以在類層級(jí)上使用 slots=True - 例如 @attr.s(slots=True) - 自動(dòng)與 attrs 聲明的 __slots__屬性匹配。所有這些功能會(huì)讓通過 attr.ib() 聲明的屬性更好更強(qiáng)大。

未來(lái)的 Python

有人為以后能普遍使用 Python 3 編程而感到高興。而我期待的是,能夠在 Python 編程時(shí)一直用attrs。就我所知,它對(duì)每個(gè)使用了的代碼庫(kù)都產(chǎn)生了積極、微妙的影響。

試試看:你可能會(huì)驚訝地發(fā)現(xiàn),以前用不方便寫文檔的元組、列表或字典的地方,現(xiàn)在可以使用具備清晰解釋的類了。既然編寫結(jié)構(gòu)清晰的類型如此簡(jiǎn)單方便,以后應(yīng)該會(huì)經(jīng)常使用 attrs 的。這對(duì)你的代碼來(lái)說是件好事;我就是一個(gè)好例子。

本譯文由 PythonTG 翻譯組出品,譯者:linkcheng,校對(duì):EarlGrey。


文章名稱:Python程序員都該用的一個(gè)庫(kù)
標(biāo)題URL:http://www.5511xx.com/article/cdshhhd.html