偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

日常Python編程代碼優(yōu)雅之道

開發(fā) 后端
Python 提供了一組獨(dú)特的工具和語言特性來使你的代碼更加優(yōu)雅、可讀和直觀。為正確的問題選擇合適的工具,你的代碼將更易于維護(hù)。在本文中,我們將研究其中的三個(gè)工具:魔術(shù)方法、迭代器和生成器,以及方法魔術(shù)。

[[237432]]

3 個(gè)可以使你的 Python 代碼更優(yōu)雅、可讀、直觀和易于維護(hù)的工具。

Python 提供了一組獨(dú)特的工具和語言特性來使你的代碼更加優(yōu)雅、可讀和直觀。為正確的問題選擇合適的工具,你的代碼將更易于維護(hù)。在本文中,我們將研究其中的三個(gè)工具:魔術(shù)方法、迭代器和生成器,以及方法魔術(shù)。

 

魔術(shù)方法

魔術(shù)方法可以看作是 Python 的管道。它們被稱為“底層”方法,用于某些內(nèi)置的方法、符號(hào)和操作。你可能熟悉的常見魔術(shù)方法是 __init__(),當(dāng)我們想要初始化一個(gè)類的新實(shí)例時(shí),它會(huì)被調(diào)用。

你可能已經(jīng)看過其他常見的魔術(shù)方法,如 __str____repr__。Python 中有一整套魔術(shù)方法,通過實(shí)現(xiàn)其中的一些方法,我們可以修改一個(gè)對(duì)象的行為,甚至使其行為類似于內(nèi)置數(shù)據(jù)類型,例如數(shù)字、列表或字典。

讓我們創(chuàng)建一個(gè) Money 類來示例:

  1. class Money:
  2. currency_rates = {
  3. '$': 1,
  4. '€': 0.88,
  5. }
  6.  
  7. def __init__(self, symbol, amount):
  8. self.symbol = symbol
  9. self.amount = amount
  10.  
  11. def __repr__(self):
  12. return '%s%.2f' % (self.symbol, self.amount)
  13.  
  14. def convert(self, other):
  15. """ Convert other amount to our currency """
  16. new_amount = (
  17. other.amount / self.currency_rates[other.symbol]
  18. * self.currency_rates[self.symbol])
  19.  
  20. return Money(self.symbol, new_amount)

該類定義為給定的貨幣符號(hào)和匯率定義了一個(gè)貨幣匯率,指定了一個(gè)初始化器(也稱為構(gòu)造函數(shù)),并實(shí)現(xiàn) __repr__,因此當(dāng)我們打印這個(gè)類時(shí),我們會(huì)看到一個(gè)友好的表示,例如 $2.00 ,這是一個(gè)帶有貨幣符號(hào)和金額的 Money('$', 2.00) 實(shí)例。最重要的是,它定義了一種方法,允許你使用不同的匯率在不同的貨幣之間進(jìn)行轉(zhuǎn)換。

打開 Python shell,假設(shè)我們已經(jīng)定義了使用兩種不同貨幣的食品的成本,如下所示:

  1. >>> soda_cost = Money('$', 5.25)
  2. >>> soda_cost
  3. $5.25
  4.  
  5. >>> pizza_cost = Money('€', 7.99)
  6. >>> pizza_cost
  7. 7.99

我們可以使用魔術(shù)方法使得這個(gè)類的實(shí)例之間可以相互交互。假設(shè)我們希望能夠?qū)⑦@個(gè)類的兩個(gè)實(shí)例一起加在一起,即使它們是不同的貨幣。為了實(shí)現(xiàn)這一點(diǎn),我們可以在 Money 類上實(shí)現(xiàn) __add__ 這個(gè)魔術(shù)方法:

  1. class Money:
  2.  
  3. # ... previously defined methods ...
  4.  
  5. def __add__(self, other):
  6. """ Add 2 Money instances using '+' """
  7. new_amount = self.amount + self.convert(other).amount
  8. return Money(self.symbol, new_amount)

現(xiàn)在我們可以以非常直觀的方式使用這個(gè)類:

  1. >>> soda_cost = Money('$', 5.25)
  2. >>> pizza_cost = Money('€', 7.99)
  3. >>> soda_cost + pizza_cost
  4. $14.33
  5. >>> pizza_cost + soda_cost
  6. 12.61

當(dāng)我們將兩個(gè)實(shí)例加在一起時(shí),我們得到以***個(gè)定義的貨幣符號(hào)所表示的結(jié)果。所有的轉(zhuǎn)換都是在底層無縫完成的。如果我們想的話,我們也可以為減法實(shí)現(xiàn) __sub__,為乘法實(shí)現(xiàn) __mul__ 等等。閱讀模擬數(shù)字類型魔術(shù)方法指南來獲得更多信息。

我們學(xué)習(xí)到 __add__ 映射到內(nèi)置運(yùn)算符 +。其他魔術(shù)方法可以映射到像 [] 這樣的符號(hào)。例如,在字典中通過索引或鍵來獲得一項(xiàng),其實(shí)是使用了 __getitem__ 方法:

  1. >>> d = {'one': 1, 'two': 2}
  2. >>> d['two']
  3. 2
  4. >>> d.__getitem__('two')
  5. 2

一些魔術(shù)方法甚至映射到內(nèi)置函數(shù),例如 __len__() 映射到 len()。

  1. class Alphabet:
  2. letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  3.  
  4. def __len__(self):
  5. return len(self.letters)
  6.  
  7. >>> my_alphabet = Alphabet()
  8. >>> len(my_alphabet)
  9. 26

 

自定義迭代器

對(duì)于新的和經(jīng)驗(yàn)豐富的 Python 開發(fā)者來說,自定義迭代器是一個(gè)非常強(qiáng)大的但令人迷惑的主題。

許多內(nèi)置類型,例如列表、集合和字典,已經(jīng)實(shí)現(xiàn)了允許它們?cè)诘讓拥膮f(xié)議。這使我們可以輕松地遍歷它們。

  1. >>> for food in ['Pizza', 'Fries']:
  2.  
  3.          print(food + '. Yum!')
  4.  
  5. Pizza. Yum!
  6. Fries. Yum!

我們?nèi)绾蔚覀冏约旱淖远x類?首先,讓我們來澄清一些術(shù)語。

  • 要成為一個(gè)可迭代對(duì)象,一個(gè)類需要實(shí)現(xiàn) __iter__()
  • __iter__() 方法需要返回一個(gè)迭代器
  • 要成為一個(gè)迭代器,一個(gè)類需要實(shí)現(xiàn) __next__()(或在 Python 2中是 next()),當(dāng)沒有更多的項(xiàng)要迭代時(shí),必須拋出一個(gè) StopIteration 異常。

呼!這聽起來很復(fù)雜,但是一旦你記住了這些基本概念,你就可以在任何時(shí)候進(jìn)行迭代。

我們什么時(shí)候想使用自定義迭代器?讓我們想象一個(gè)場(chǎng)景,我們有一個(gè) Server 實(shí)例在不同的端口上運(yùn)行不同的服務(wù),如 httpssh。其中一些服務(wù)處于 active 狀態(tài),而其他服務(wù)則處于 inactive 狀態(tài)。

  1. class Server:
  2.  
  3. services = [
  4. {'active': False, 'protocol': 'ftp', 'port': 21},
  5. {'active': True, 'protocol': 'ssh', 'port': 22},
  6. {'active': True, 'protocol': 'http', 'port': 80},
  7. ]

當(dāng)我們遍歷 Server 實(shí)例時(shí),我們只想遍歷那些處于 active 的服務(wù)。讓我們創(chuàng)建一個(gè) IterableServer 類:

  1. class IterableServer:
  2.     def __init__(self):
  3.         self.current_pos = 0
  4.     def __next__(self):
  5.         pass  # TODO: 實(shí)現(xiàn)并記得拋出 StopIteration

首先,我們將當(dāng)前位置初始化為 0。然后,我們定義一個(gè) __next__() 方法來返回下一項(xiàng)。我們還將確保在沒有更多項(xiàng)返回時(shí)拋出 StopIteration。到目前為止都很好!現(xiàn)在,讓我們實(shí)現(xiàn)這個(gè) __next__() 方法。

  1. class IterableServer:
  2.     def __init__(self):
  3.         self.current_pos = 0.  # 我們初始化當(dāng)前位置為 0
  4.     def __iter__(self):  # 我們可以在這里返回 self,因?yàn)閷?shí)現(xiàn)了 __next__
  5.         return self
  6.     def __next__(self):
  7.         while self.current_pos < len(self.services):
  8.             service = self.services[self.current_pos]
  9.             self.current_pos += 1
  10.             if service['active']:
  11.                 return service['protocol'], service['port']
  12.         raise StopIteration
  13.     next = __next__  # 可選的 Python2 兼容性

我們對(duì)列表中的服務(wù)進(jìn)行遍歷,而當(dāng)前的位置小于服務(wù)的個(gè)數(shù),但只有在服務(wù)處于活動(dòng)狀態(tài)時(shí)才返回。一旦我們遍歷完服務(wù),就會(huì)拋出一個(gè) StopIteration 異常。

因?yàn)槲覀儗?shí)現(xiàn)了 __next__() 方法,當(dāng)它耗盡時(shí),它會(huì)拋出 StopIteration。我們可以從 __iter__() 返回 self,因?yàn)?IterableServer 類遵循 iterable 協(xié)議。

現(xiàn)在我們可以遍歷一個(gè) IterableServer 實(shí)例,這將允許我們查看每個(gè)處于活動(dòng)的服務(wù),如下所示:

  1. >>> for protocol, port in IterableServer():
  2.  
  3.         print('service %s is running on port %d' % (protocol, port))
  4.  
  5. service ssh is running on port 22
  6.  
  7. service http is running on port 21
  8.  

太棒了,但我們可以做得更好!在這樣類似的實(shí)例中,我們的迭代器不需要維護(hù)大量的狀態(tài),我們可以簡(jiǎn)化代碼并使用 generator(生成器) 來代替。

  1. class Server:
  2.     services = [
  3.         {'active': False, 'protocol': 'ftp', 'port': 21},
  4.         {'active': True, 'protocol': 'ssh', 'port': 22},
  5.         {'active': True, 'protocol': 'http', 'port': 21},
  6.     ]
  7.     def __iter__(self):
  8.         for service in self.services:
  9.             if service['active']:
  10.                 yield service['protocol'], service['port']

yield 關(guān)鍵字到底是什么?在定義生成器函數(shù)時(shí)使用 yield。這有點(diǎn)像 return,雖然 return 在返回值后退出函數(shù),但 yield 會(huì)暫停執(zhí)行直到下次調(diào)用它。這允許你的生成器的功能在它恢復(fù)之前保持狀態(tài)。查看 yield 的文檔以了解更多信息。使用生成器,我們不必通過記住我們的位置來手動(dòng)維護(hù)狀態(tài)。生成器只知道兩件事:它現(xiàn)在需要做什么以及計(jì)算下一個(gè)項(xiàng)目需要做什么。一旦我們到達(dá)執(zhí)行點(diǎn),即 yield 不再被調(diào)用,我們就知道停止迭代。

這是因?yàn)橐恍﹥?nèi)置的 Python 魔法。在 Python 關(guān)于 __iter__() 的文檔中我們可以看到,如果 __iter__() 是作為一個(gè)生成器實(shí)現(xiàn)的,它將自動(dòng)返回一個(gè)迭代器對(duì)象,該對(duì)象提供 __iter__()__next__() 方法。閱讀這篇很棒的文章,深入了解迭代器,可迭代對(duì)象和生成器。

 

方法魔法

由于其獨(dú)特的方面,Python 提供了一些有趣的方法魔法作為語言的一部分。

其中一個(gè)例子是別名功能。因?yàn)楹瘮?shù)只是對(duì)象,所以我們可以將它們賦值給多個(gè)變量。例如:

  1. >>> def foo():
  2.        return 'foo'
  3. >>> foo()
  4. 'foo'
  5. >>> bar = foo
  6. >>> bar()
  7. 'foo'

我們稍后會(huì)看到它的作用。

Python 提供了一個(gè)方便的內(nèi)置函數(shù)稱為 getattr(),它接受 object, name, default 參數(shù)并在 object 上返回屬性 name。這種編程方式允許我們?cè)L問實(shí)例變量和方法。例如:

  1. >>> class Dog:
  2. sound = 'Bark'
  3. def speak(self):
  4. print(self.sound + '!', self.sound + '!')
  5.  
  6. >>> fido = Dog()
  7.  
  8. >>> fido.sound
  9. 'Bark'
  10. >>> getattr(fido, 'sound')
  11. 'Bark'
  12.  
  13. >>> fido.speak
  14. <bound method Dog.speak of <__main__.Dog object at 0x102db8828>>
  15. >>> getattr(fido, 'speak')
  16. <bound method Dog.speak of <__main__.Dog object at 0x102db8828>>
  17.  
  18.  
  19. >>> fido.speak()
  20. Bark! Bark!
  21. >>> speak_method = getattr(fido, 'speak')
  22. >>> speak_method()
  23. Bark! Bark!

這是一個(gè)很酷的技巧,但是我們?nèi)绾卧趯?shí)際中使用 getattr 呢?讓我們看一個(gè)例子,我們編寫一個(gè)小型命令行工具來動(dòng)態(tài)處理命令。

  1. class Operations:
  2.     def say_hi(self, name):
  3.         print('Hello,', name)
  4.     def say_bye(self, name):
  5.         print ('Goodbye,', name)
  6.     def default(self, arg):
  7.         print ('This operation is not supported.')
  8.  
  9. if __name__ == '__main__':
  10.     operations = Operations()
  11.     # 假設(shè)我們做了錯(cuò)誤處理
  12.     command, argument = input('> ').split()
  13.     func_to_call = getattr(operations, command, operations.default)
  14.     func_to_call(argument)

腳本的輸出是:

  1. $ python getattr.py
  2. > say_hi Nina
  3. Hello, Nina
  4. > blah blah
  5. This operation is not supported.

接下來,我們來看看 partial。例如,functool.partial(func, *args, **kwargs) 允許你返回一個(gè)新的 partial 對(duì)象,它的行為類似 func,參數(shù)是 argskwargs。如果傳入更多的 args,它們會(huì)被附加到 args。如果傳入更多的 kwargs,它們會(huì)擴(kuò)展并覆蓋 kwargs。讓我們通過一個(gè)簡(jiǎn)短的例子來看看:

  1. >>> from functools import partial
  2. >>> basetwo = partial(int, base=2)
  3. >>> basetwo
  4. <functools.partial object at 0x1085a09f0>
  5. >>> basetwo('10010')
  6. 18
  7.  
  8. # 這等同于
  9. >>> int('10010', base=2)

讓我們看看在我喜歡的一個(gè)名為 agithub 的庫中的一些示例代碼中,這個(gè)方法魔術(shù)是如何結(jié)合在一起的,這是一個(gè)(名字起得很 low 的) REST API 客戶端,它具有透明的語法,允許你以最小的配置快速構(gòu)建任何 REST API 原型(不僅僅是 GitHub)。我發(fā)現(xiàn)這個(gè)項(xiàng)目很有趣,因?yàn)樗浅?qiáng)大,但只有大約 400 行 Python 代碼。你可以在大約 30 行配置代碼中添加對(duì)任何 REST API 的支持。agithub 知道協(xié)議所需的一切(REST、HTTP、TCP),但它不考慮上游 API。讓我們深入到它的實(shí)現(xiàn)中。

以下是我們?nèi)绾螢?GitHub API 和任何其他相關(guān)連接屬性定義端點(diǎn) URL 的簡(jiǎn)化版本。在這里查看完整代碼。

  1. class GitHub(API):
  2.     def __init__(self, token=None, *args, **kwargs):
  3.         props = ConnectionProperties(api_url = kwargs.pop('api_url', 'api.github.com'))
  4.         self.setClient(Client(*args, **kwargs))
  5.         self.setConnectionProperties(props)

然后,一旦配置了訪問令牌,就可以開始使用 GitHub API

  1. >>> gh = GitHub('token')
  2. >>> status, data = gh.user.repos.get(visibility='public', sort='created')
  3. >>> # ^ 映射到 GET /user/repos
  4. >>> data
  5. ... ['tweeter', 'snipey', '...']

請(qǐng)注意,你要確保 URL 拼寫正確,因?yàn)槲覀儧]有驗(yàn)證 URL。如果 URL 不存在或出現(xiàn)了其他任何錯(cuò)誤,將返回 API 拋出的錯(cuò)誤。那么,這一切是如何運(yùn)作的呢?讓我們找出答案。首先,我們將查看一個(gè) API的簡(jiǎn)化示例:

  1. class API:
  2.     # ... other methods ...
  3.     def __getattr__(self, key):
  4.         return IncompleteRequest(self.client).__getattr__(key)
  5.     __getitem__ = __getattr__

API 類上的每次調(diào)用都會(huì)調(diào)用 IncompleteRequest作為指定的 key。

  1. class IncompleteRequest:
  2.     # ... other methods ...
  3.     def __getattr__(self, key):
  4.         if key in self.client.http_methods:
  5.             htmlMethod = getattr(self.client, key)
  6.             return partial(htmlMethod, url=self.url)
  7.         else:
  8.             self.url += '/' + str(key)
  9.             return self
  10.     __getitem__ = __getattr__
  11.  
  12. class Client:
  13.     http_methods = ('get')  # 還有 post, put, patch 等等。
  14.     def get(self, url, headers={}, **params):
  15.         return self.request('GET', url, None, headers)

如果***一次調(diào)用不是 HTTP 方法(如 get、post 等),則返回帶有附加路徑的 IncompleteRequest。否則,它從Client獲取 HTTP 方法對(duì)應(yīng)的正確函數(shù),并返回 partial。

如果我們給出一個(gè)不存在的路徑會(huì)發(fā)生什么?

  1. >>> status, data = this.path.doesnt.exist.get()
  2. >>> status
  3. ... 404

因?yàn)?__getattr__ 別名為 __getitem__

  1. >>> owner, repo = 'nnja', 'tweeter'
  2. >>> status, data = gh.repos[owner][repo].pulls.get()
  3. >>> # ^ Maps to GET /repos/nnja/tweeter/pulls
  4. >>> data
  5. .... # {....}

這真心是一些方法魔術(shù)!

 

了解更多

Python 提供了大量工具,使你的代碼更優(yōu)雅,更易于閱讀和理解。挑戰(zhàn)在于找到合適的工具來完成工作,但我希望本文為你的工具箱添加了一些新工具。而且,如果你想更進(jìn)一步,你可以在我的博客 nnja.io 上閱讀有關(guān)裝飾器、上下文管理器、上下文生成器和命名元組的內(nèi)容。隨著你成為一名更好的 Python 開發(fā)人員,我鼓勵(lì)你到那里閱讀一些設(shè)計(jì)良好的項(xiàng)目的源代碼。RequestsFlask 是兩個(gè)很好的起步的代碼庫。 

責(zé)任編輯:龐桂玉 來源: Linux中國
相關(guān)推薦

2023-08-04 08:52:52

Optional消滅空指針

2024-06-19 10:04:15

ifC#代碼

2022-07-06 08:39:33

Python代碼

2018-09-18 16:20:08

Asyncjavascript前端

2023-11-23 13:50:00

Python代碼

2024-07-25 14:36:10

2012-08-01 09:38:17

代碼整潔

2023-07-10 09:39:02

lambdaPython語言

2022-09-16 09:11:30

C++代碼編程

2025-09-10 07:15:00

Python編程語言對(duì)象編程

2022-08-31 12:15:09

JavaScript代碼優(yōu)化

2012-08-01 09:23:31

代碼

2021-01-06 14:42:09

前端Typescript代碼

2025-08-04 06:00:00

Java并發(fā)編程開發(fā)

2022-09-19 15:02:24

C語言

2024-11-28 11:34:54

2010-07-16 14:22:25

Python teln

2017-06-26 09:40:50

Python代碼寫法

2017-07-07 16:57:35

代碼Python

2014-12-26 10:06:48

Docker容器代碼部署
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)