欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Python 3.8中實(shí)現(xiàn)functools.cached_property功能

 更新時(shí)間:2019年05月29日 10:18:14   作者:小明明S À DOMICILE  
這篇文章主要介紹了Python 3.8中實(shí)現(xiàn)functools.cached_property功能,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

前言

緩存屬性( cached_property )是一個(gè)非常常用的功能,很多知名Python項(xiàng)目都自己實(shí)現(xiàn)過(guò)它。我舉幾個(gè)例子:

bottle.cached_property

Bottle是我最早接觸的Web框架,也是我第一次閱讀的開(kāi)源項(xiàng)目源碼。最早知道 cached_property 就是通過(guò)這個(gè)項(xiàng)目,如果你是一個(gè)Web開(kāi)發(fā),我不建議你用這個(gè)框架,但是源碼量少,值得一讀~

werkzeug.utils.cached_property

Werkzeug是Flask的依賴,是應(yīng)用 cached_property 最成功的一個(gè)項(xiàng)目。代碼見(jiàn)延伸閱讀鏈接2

pip._vendor.distlib.util.cached_property

PIP是Python官方包管理工具。代碼見(jiàn)延伸閱讀鏈接3

kombu.utils.objects.cached_property

Kombu是Celery的依賴。代碼見(jiàn)延伸閱讀鏈接4

django.utils.functional.cached_property

Django是知名Web框架,你肯定聽(tīng)過(guò)。代碼見(jiàn)延伸閱讀鏈接5

甚至有專門的一個(gè)包: pydanny/cached-property ,延伸閱讀6

如果你犯過(guò)他們的代碼其實(shí)大同小異,在我的觀點(diǎn)里面這種輪子是完全沒(méi)有必要的。Python 3.8給 functools 模塊添加了 cached_property 類,這樣就有了官方的實(shí)現(xiàn)了

PS: 其實(shí)這個(gè)Issue 2014年就建立了,5年才被Merge!

Python 3.8的cached_property

借著這個(gè)小章節(jié)我們了解下怎么使用以及它的作用(其實(shí)看名字你可能已經(jīng)猜出來(lái)):

./python.exe
Python 3.8.0a4+ (heads/master:9ee2c264c3, May 28 2019, 17:44:24)
[Clang 10.0.0 (clang-1000.11.45.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from functools import cached_property
>>> class Foo:
...   @cached_property
...   def bar(self):
...     print('calculate somethings')
...     return 42
...
>>> f = Foo()
>>> f.bar
calculate somethings
42
>>> f.bar
42

上面的例子中首先獲得了Foo的實(shí)例f,第一次獲得 f.bar 時(shí)可以看到執(zhí)行了bar方法的邏輯(因?yàn)閳?zhí)行了print語(yǔ)句),之后再獲得 f.bar 的值并不會(huì)在執(zhí)行bar方法,而是用了緩存的屬性的值。

標(biāo)準(zhǔn)庫(kù)中的版本還有一種的特點(diǎn),就是加了線程鎖,防止多個(gè)線程一起修改緩存。通過(guò)對(duì)比Werkzeug里的實(shí)現(xiàn)幫助大家理解一下:

import time
from threading import Thread
from werkzeug.utils import cached_property
class Foo:
  def __init__(self):
    self.count = 0
  @cached_property
  def bar(self):
    time.sleep(1) # 模仿耗時(shí)的邏輯,讓多線程啟動(dòng)后能執(zhí)行一會(huì)而不是直接結(jié)束
    self.count += 1
    return self.count
threads = []
f = Foo()
for x in range(10):
  t = Thread(target=lambda: f.bar)
  t.start()
  threads.append(t)
for t in threads:
  t.join()

這個(gè)例子中,bar方法對(duì) self.count 做了自增1的操作,然后返回。但是注意f.bar的訪問(wèn)是在10個(gè)線程下進(jìn)行的,里面大家猜現(xiàn)在 f.bar 的值是多少?

 ipython -i threaded_cached_property.py
Python 3.7.1 (default, Dec 13 2018, 22:28:16)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.5.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]: f.bar
Out[1]: 10

結(jié)果是10。也就是10個(gè)線程同時(shí)訪問(wèn) f.bar ,每個(gè)線程中訪問(wèn)時(shí)由于都還沒(méi)有緩存,就會(huì)給 f.count 做自增1操作。第三方庫(kù)對(duì)于這個(gè)問(wèn)題可以不關(guān)注,只要你確保在項(xiàng)目中不出現(xiàn)多線程并發(fā)訪問(wèn)場(chǎng)景即可。但是對(duì)于標(biāo)準(zhǔn)庫(kù)來(lái)說(shuō),需要考慮的更周全。我們把 cached_property 改成從標(biāo)準(zhǔn)庫(kù)導(dǎo)入,感受下:

./python.exe
Python 3.8.0a4+ (heads/master:8cd5165ba0, May 27 2019, 22:28:15)
[Clang 10.0.0 (clang-1000.11.45.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import time
>>> from threading import Thread
>>> from functools import cached_property
>>>
>>>
>>> class Foo:
...   def __init__(self):
...     self.count = 0
...   @cached_property
...   def bar(self):
...     time.sleep(1)
...     self.count += 1
...     return self.count
...
>>>
>>> threads = []
>>> f = Foo()
>>>
>>> for x in range(10):
...   t = Thread(target=lambda: f.bar)
...   t.start()
...   threads.append(t)
...
>>> for t in threads:
...   t.join()
...
>>> f.bar

可以看到,由于加了線程鎖, f.bar 的結(jié)果是正確的1。

cached_property不支持異步

除了 pydanny/cached-property 這個(gè)包以外,其他的包都不支持異步函數(shù):

./python.exe -m asyncio
asyncio REPL 3.8.0a4+ (heads/master:8cd5165ba0, May 27 2019, 22:28:15)
[Clang 10.0.0 (clang-1000.11.45.5)] on darwin
Use "await" directly instead of "asyncio.run()".
Type "help", "copyright", "credits" or "license" for more information.
>>> import asyncio
>>> from functools import cached_property
>>>
>>>
>>> class Foo:
...   def __init__(self):
...     self.count = 0
...   @cached_property
...   async def bar(self):
...     await asyncio.sleep(1)
...     self.count += 1
...     return self.count
...
>>> f = Foo()
>>> await f.bar
1
>>> await f.bar
Traceback (most recent call last):
 File "/Users/dongwm/cpython/Lib/concurrent/futures/_base.py", line 439, in result
  return self.__get_result()
 File "/Users/dongwm/cpython/Lib/concurrent/futures/_base.py", line 388, in __get_result
  raise self._exception
 File "<console>", line 1, in <module>
RuntimeError: cannot reuse already awaited coroutine
pydanny/cached-property的異步支持實(shí)現(xiàn)的很巧妙,我把這部分邏輯抽出來(lái):
try:
  import asyncio
except (ImportError, SyntaxError):
  asyncio = None
class cached_property:
  def __get__(self, obj, cls):
    ...
    if asyncio and asyncio.iscoroutinefunction(self.func):
      return self._wrap_in_coroutine(obj)
    ...
  def _wrap_in_coroutine(self, obj):
    @asyncio.coroutine
    def wrapper():
      future = asyncio.ensure_future(self.func(obj))
      obj.__dict__[self.func.__name__] = future
      return future
    return wrapper()

我解析一下這段代碼:

對(duì) import asyncio 的異常處理主要為了處理Python 2和Python3.4之前沒(méi)有asyncio的問(wèn)題

__get__ 里面會(huì)判斷方法是不是協(xié)程函數(shù),如果是會(huì) return self._wrap_in_coroutine(obj)
_wrap_in_coroutine 里面首先會(huì)把方法封裝成一個(gè)Task,并把Task對(duì)象緩存在 obj.__dict__ 里,wrapper通過(guò)裝飾器 asyncio.coroutine 包裝最后返回。

為了方便理解,在IPython運(yùn)行一下:

In : f = Foo()

In : f.bar  # 由于用了`asyncio.coroutine`裝飾器,這是一個(gè)生成器對(duì)象
Out: <generator object cached_property._wrap_in_coroutine.<locals>.wrapper at 0x10a26f0c0>

In : await f.bar  # 第一次獲得f.bar的值,會(huì)sleep 1秒然后返回結(jié)果
Out: 1

In : f.__dict__['bar']  # 這樣就把Task對(duì)象緩存到了f.__dict__里面了,Task狀態(tài)是finished
Out: <Task finished coro=<Foo.bar() done, defined at <ipython-input-54-7f5df0e2b4e7>:4> result=1>

In : f.bar  # f.bar已經(jīng)是一個(gè)task了
Out: <Task finished coro=<Foo.bar() done, defined at <ipython-input-54-7f5df0e2b4e7>:4> result=1>

In : await f.bar  # 相當(dāng)于 await task
Out: 1

可以看到多次await都可以獲得正常結(jié)果。如果一個(gè)Task對(duì)象已經(jīng)是finished狀態(tài),直接返回結(jié)果而不會(huì)重復(fù)執(zhí)行了。

總結(jié)

以上所述是小編給大家介紹的Python 3.8中實(shí)現(xiàn)functools.cached_property功能,希望對(duì)大家有所幫助,如果大家有任何疑問(wèn)請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
如果你覺(jué)得本文對(duì)你有幫助,歡迎轉(zhuǎn)載,煩請(qǐng)注明出處,謝謝!

相關(guān)文章

  • Python中UserWarning:The NumPy module was reloaded問(wèn)題的解決方案

    Python中UserWarning:The NumPy module was 

    在 Python 項(xiàng)目中,我們經(jīng)常需要導(dǎo)入許多庫(kù)來(lái)完成各種任務(wù),NumPy 作為一個(gè)核心的科學(xué)計(jì)算庫(kù),被廣泛應(yīng)用于數(shù)據(jù)處理和分析,然而,有時(shí)我們會(huì)遇到 NumPy 重載的警告,本文將詳細(xì)講解這一警告的原因,并提供解決方案,需要的朋友可以參考下
    2024-07-07
  • pip版本低引發(fā)的python離線包安裝失敗的問(wèn)題

    pip版本低引發(fā)的python離線包安裝失敗的問(wèn)題

    這篇文章主要介紹了pip版本低引發(fā)的python離線包安裝失敗的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • Python內(nèi)存管理方式和垃圾回收算法解析

    Python內(nèi)存管理方式和垃圾回收算法解析

    這篇文章主要介紹了Python內(nèi)存管理方式和垃圾回收算法解析,介紹了傳統(tǒng)的垃圾回收機(jī)制,其工作方法,finalizer的問(wèn)題等相關(guān)內(nèi)容,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11
  • python批量修改交換機(jī)密碼的示例

    python批量修改交換機(jī)密碼的示例

    這篇文章主要介紹了python批量修改交換機(jī)密碼的示例,幫助大家更好的理解和使用python,感興趣的朋友可以了解下
    2020-09-09
  • Tensorflow?2.4加載處理圖片的三種方式詳解

    Tensorflow?2.4加載處理圖片的三種方式詳解

    這篇文章主要為大家介紹了Tensorflow?2.4加載處理圖片的三種方式詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11
  • keras和tensorflow使用fit_generator 批次訓(xùn)練操作

    keras和tensorflow使用fit_generator 批次訓(xùn)練操作

    這篇文章主要介紹了keras和tensorflow使用fit_generator 批次訓(xùn)練操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-07-07
  • Python進(jìn)度條神器tqdm使用實(shí)例詳解

    Python進(jìn)度條神器tqdm使用實(shí)例詳解

    Python進(jìn)度條神器tqdm是一個(gè)快速、可擴(kuò)展的進(jìn)度條工具,可以輕松地為Python腳本添加進(jìn)度條。它可以在循環(huán)中自動(dòng)計(jì)算進(jìn)度,并在終端中顯示進(jìn)度條,讓用戶了解程序的運(yùn)行情況。tqdm還支持多線程和多進(jìn)程,并且可以自定義進(jìn)度條的樣式和顯示方式。
    2023-06-06
  • 對(duì)tensorflow中cifar-10文檔的Read操作詳解

    對(duì)tensorflow中cifar-10文檔的Read操作詳解

    今天小編就為大家分享一篇對(duì)tensorflow中cifar-10文檔的Read操作詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-02-02
  • python制作小說(shuō)爬蟲(chóng)實(shí)錄

    python制作小說(shuō)爬蟲(chóng)實(shí)錄

    本文給大家介紹的是作者所寫(xiě)的第一個(gè)爬蟲(chóng)程序的全過(guò)程,從構(gòu)思到思路到程序的編寫(xiě),非常的細(xì)致,有需要的小伙伴可以參考下
    2017-08-08
  • 在Ubuntu系統(tǒng)中運(yùn)行python代碼的幾個(gè)步驟

    在Ubuntu系統(tǒng)中運(yùn)行python代碼的幾個(gè)步驟

    項(xiàng)目中需要在Linux上運(yùn)行自己寫(xiě)的python腳本,特此記錄一下操作流程,整個(gè)流程比較簡(jiǎn)單,下面這篇文章主要給大家介紹了關(guān)于在Ubuntu系統(tǒng)中運(yùn)行python代碼的幾個(gè)步驟,需要的朋友可以參考下
    2023-12-12

最新評(píng)論