­

python的這些小知識你注意到了嗎?

  • 2020 年 3 月 11 日
  • 筆記

1.操作文件的 x 模式

對文件進行操作的 opend 函數大家都很熟悉,但是你注意到它還有一種模式為 x 嗎?

今天閑的無聊,翻了翻 python 的文檔(小閆電腦保存的 python 文檔為3.8.2版本),然後發現:不光有 awbr+ 模式,竟然還有一種模式為 x,下面講一下吧。

官方文檔稱:在 3.3 版本中,增加了 x 模式,它表示創建一個文件,如果文件已經存在,會報錯 FileExistsError 。還需要注意一點,在 3.3 版本以後,曾經的 IOError 被別名為 OSError。更多精彩文章請關注公眾號『Pythonnote』或者『全棧技術精選』

2.協程庫 asyncio

之前常用第三方庫 greentletgevent 來創建協程,後來在 3.4 版本後,python 自身引入了一個協程庫 asyncio 。它用來實現非同步 I/O。下面看一下示例程式碼:

import asyncio  async def main_one():    print('hello ...')    await asyncio.sleep(3)    print('... main_one!')  async def main_two():    print('hello ...')    await asyncio.sleep(3)    print('... main_two!')  # 創建 task 列表。ensure_future() 函數會返回 task 對象tasks = [asyncio.ensure_future(main_one()),asyncio.ensure_future(main_two())]# 使用 get_event_loop() 方法創建一個事件循環loop = asyncio.get_event_loop()# 多個任務(即 task 列表)使用 wait() 方法執行# 然後再調用 run_until_complete() 函數將協程註冊到事件循環中loop.run_until_complete(asyncio.wait(tasks))

結果為:

hello ...hello ...... main_one!... main_two!

3.裝飾器 wraps

模組 functools 中的 wraps ,會讓使用了裝飾器的函數偽裝的更像原函數,也就是將原函數的屬性賦值給使用了裝飾器後的原函數。這句話可能比較難理解,我們先來回顧一下什麼是裝飾器?裝飾器就是在不改變現有函數基礎上,為函數增加功能的一個函數,它使用閉包來實現。閉包的規則為在函數內部定義了一個函數,內部函數使用了外部函數的變數,外部函數返回了內部函數的引用。有一點需要注意:雖然被裝飾器裝飾的函數仍然可以使用原名稱調用,但是其實質上指向了裝飾器的內函數。通過以下示例進行說明:

1) 先來一個普通的裝飾器使用示例更多精彩文章請關注公眾號『Pythonnote』或者『全棧技術精選』

# 定義一個裝飾器def eg_decorator(func):    def wrapper(*args, **kwds):        print('我是裝飾器啊...')        return func(*args, **kwds)    return wrapper  # 使用定義的裝飾器裝飾函數@eg_decoratordef noname():    print('我是示例函數噻...')  # 調用函數noname()# 列印函數的 __name__ 屬性print(noname.__name__)  """結果是:我是裝飾器啊...我是示例函數噻...wrapper"""

可以看到函數 noname() 的屬性 __name__ 的值為 wrapper ,即函數 noname() 指向的是裝飾器的內函數。

2) 接下來請出今天的主角 wraps

from functools import wraps  # <-- 導入模組  # 定義一個裝飾器def eg_decorator(func):    @wraps(func)    # <-- 使用裝飾器    def wrapper(*args, **kwds):        print('我是裝飾器啊...')        return func(*args, **kwds)    return wrapper  # 使用定義的裝飾器裝飾函數@eg_decoratordef noname():    print('我是示例函數噻...')  # 調用函數noname()# 列印函數的 __name__ 屬性print(noname.__name__)  """結果是:我是裝飾器啊...我是示例函數噻...noname"""

可以看到 __name__ 屬性的值被修改為函數 noname 自己。

也許大家會問這有什麼用?很久很久以前,在一個 Flask 項目中曾經出現過一個 bug :給視圖函數添加了自定義的裝飾器之後(該裝飾器會驗證用戶是否登錄),再用 @app.route("/index") 綁定路徑,會報錯「你添加路徑的視圖函數已經綁定了其他路徑」。這是因為路徑會綁定自定義裝飾器的內函數,之前其他視圖綁定的其實並不是視圖本身,而是自定義裝飾器的內函數,這次實質上也去綁定內函數,所以會報錯。通過查看底層程式碼是因為 Flask 的路由地址根據屬性 __name__ 綁定視圖名稱。