成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Python 隱藏法寶:雙下劃線 _ _Dunder_ _

開發(fā) 前端
對于大多數(shù) Python 開發(fā)者來說,他們遇到的第一個 dunder 是?__init__,構(gòu)造函數(shù)方法。當您創(chuàng)建一個類的實例時,這個方法會被自動調(diào)用,使用熟悉的語法?MyClass(*args, **kwargs)作為顯式調(diào)用?MyClass.__init__(*args, **kwargs)?的快捷方式。

你可能不知道,Python里那些用雙下劃線包裹的"魔法方法"(Dunder方法),其實是提升代碼質(zhì)量的絕佳工具。但有趣的是,很多經(jīng)驗豐富的開發(fā)者對這些方法也只是一知半解。

先說句公道話: 這其實情有可原。因為在多數(shù)情況下,Dunder方法的作用是"錦上添花"——它們能讓代碼更簡潔規(guī)范,但不用它們也能完成任務(wù)。有時候我們甚至不知不覺就在使用這些特殊方法了。

如果你符合以下任一情況:

  • 經(jīng)常用Python但不太了解這個特性
  • 像我一樣癡迷編程語言的精妙設(shè)計
  • 想讓代碼既專業(yè)又優(yōu)雅

那么,這篇文章就是為你準備的!我們將探索如何巧妙運用這些"魔法方法"來:

  • 大幅簡化代碼邏輯
  • 提升代碼可讀性
  • 寫出更Pythonic的優(yōu)雅代碼

表象會騙人......即使在 Python 中也是如此!

如果說我在生活中學到了什么,那就是并非所有東西都像第一眼看上去那樣,Python 也不例外。

看一個看似簡單的例子:

class EmptyClass:
  pass

這是我們可以在 Python 中定義的最 “空” 的自定義類,因為我們沒有定義屬性或方法。它是如此的空,你會認為你什么也做不了。

然而,事實并非如此。例如,如果您嘗試創(chuàng)建該類的實例,甚至比較兩個實例是否相等,Python 都不會抱怨:

empty_instance = EmptyClass()
another_empty_instance = EmptyClass()
empty_instance == another_empty_instance
False

當然,這并不是魔法。簡單地說,利用標準的 object 接口,Python 中的任何對象都繼承了一些默認屬性和方法,這些屬性和方法可以讓用戶與之進行最少的交互。

雖然這些方法看起來是隱藏的,但它們并不是不可見的。要訪問可用的方法,包括 Python 自己分配的方法,只需使用 dir() 內(nèi)置函數(shù)。對于我們的空類,我們得到

>>> dir(EmptyClass)
['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', 
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', 
'__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', 
'__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', 
'__str__', '__subclasshook__', '__weakref__']

正是這些方法可以解釋我們之前觀察到的行為。例如,由于該類實際上有一個__init__方法,我們就不應(yīng)該對我們可以實例化一個該類的對象感到驚訝。

Dunder方法

最后輸出中顯示的所有方法都屬于一個特殊的群體--猜猜看--dunder 方法。dunder 是雙下劃線(double underscore)的縮寫,指的是這些方法名稱開頭和結(jié)尾的雙下劃線。

它們之所以特殊,有以下幾個原因:

  1. 它們內(nèi)置于每個對象中:每個 Python 對象都配備了由其類型決定的一組特定的 dunder 方法。
  2. 它們是隱式調(diào)用的:許多 dunder 方法是通過與 Python 本機運算符或內(nèi)置函數(shù)的交互自動觸發(fā)的。例如,用 == 比較兩個對象相當于調(diào)用它們的 __eq__方法。
  3. 它們是可定制的:您可以覆蓋現(xiàn)有的 dunder 方法,或者為您的類定義新的方法,以便在保留隱式調(diào)用的同時賦予它們自定義的行為。

對于大多數(shù) Python 開發(fā)者來說,他們遇到的第一個 dunder 是 __init__,構(gòu)造函數(shù)方法。當您創(chuàng)建一個類的實例時,這個方法會被自動調(diào)用,使用熟悉的語法 MyClass(*args, **kwargs)作為顯式調(diào)用 MyClass.__init__(*args, **kwargs) 的快捷方式。

盡管是最常用的方法,__init__ 也是最專業(yè)的 dunder 方法之一。它沒有充分展示 dunder 方法的靈活性和強大功能,而這些方法可以讓您重新定義對象與原生 Python 特性的交互方式。

使對象漂亮

定義一個類來表示商店中出售的物品,并通過指定名稱和價格來創(chuàng)建一個實例。

class Item:
    def __init__(self, name: str, price: float) -> None:
        self.name = name
        self.price = price


item = Item(name="Milk (1L)", price=0.99)

如果我們嘗試顯示 item 變量的內(nèi)容,會發(fā)生什么?現(xiàn)在,Python 所能做的就是告訴我們它是什么類型的對象,以及它在內(nèi)存中的分配位置:

item
<__main__.Item at 0x00000226C614E870>

試著得到一個信息量更大、更漂亮的輸出!

要做到這一點,我們可以覆蓋 __repr__ dunder,當在交互式 Python 控制臺中鍵入一個類實例時,它的輸出將完全是打印出來的,而且--只要沒有覆蓋另一個 dunder 方法 __str__ --當試圖調(diào)用 print() 時也是如此。

注意:通常的做法是讓 __repr__ 提供重新創(chuàng)建打印實例所需的語法。因此,在后一種情況下,我們希望輸出Item(name="Milk(1L)", price=0.99)

class Item:
    def __init__(self, name: str, price: float) -> None:
        self.name = name
        self.price = price

    def __repr__(self) -> str:
        return f"{self.__class__.__name__}('{self.name}', {self.price})"


item = Item(name="Milk (1L)", price=0.99)

item # In this example it is equivalent also to the command: print(item)
Item('Milk (1L)', 0.99)

沒什么特別的吧?你說得沒錯:我們本可以實現(xiàn)同樣的方法,并將其命名為 *my_custom_repr*,而不需要使用indo dunder 方法。然而,雖然任何人都能立即理解 print(item) 或 item 的意思,但 item.my_custom_repr() 這樣的方法也能理解嗎?

定義對象與 Python 本地運算符之間的交互

假設(shè)我們想創(chuàng)建一個新類,即 Grocery,它允許我們建立一個 Item 及其數(shù)量的集合。

在這種情況下,我們可以使用 dunder 方法來進行一些標準操作,例如

  1. 使用 + 運算符將特定數(shù)量的 Item 添加到 Grocery 中
  2. 使用 for 循環(huán)直接遍歷 Grocery 類
  3. 使用括號 [] 符號從 Grocery 類中訪問特定的 Item

為了實現(xiàn)這一目標,我們將定義(我們已經(jīng)看到泛型類默認情況下沒有這些方法)dunder 方法 __add____iter__ 和__getitem__

from typing import Optional, Iterator
from typing_extensions import Self


class Grocery:

    def __init__(self, items: Optional[dict[Item, int]] = None):
        self.items = items or dict()

    def __add__(self, new_items: dict[Item, int]) -> Self:

        new_grocery = Grocery(items=self.items)

        for new_item, quantity in new_items.items():

            if new_item in new_grocery.items:
                new_grocery.items[new_item] += quantity
            else:
                new_grocery.items[new_item] = quantity

        return new_grocery

    def __iter__(self) -> Iterator[Item]:
        return iter(self.items)

    def __getitem__(self, item: Item) -> int:

        if self.items.get(item):
            return self.items.get(item)
        else:
            raise KeyError(f"Item {item} not in the grocery")

初始化一個 Grocery 實例,并打印其主要屬性 items. 的內(nèi)容。

item = Item(name="Milk (1L)", price=0.99)
grocery = Grocery(items={item: 3})

print(grocery.items)
{Item('Milk (1L)', 0.99): 3}

然后,我們使用 + 運算符添加一個新項目,并驗證更改是否已生效。

new_item = Item(name="Soy Sauce (0.375L)", price=1.99)
grocery = grocery + {new_item: 1} + {item: 2}

print(grocery.items)
{Item('Milk (1L)', 0.99): 5, Item('Soy Sauce (0.375L)', 1.99): 1}

既友好又明確,對嗎?

通過 __iter__ 方法,我們可以按照該方法中實現(xiàn)的邏輯對一個 Grocery 對象進行循環(huán)(即,隱式循環(huán)將遍歷可遍歷屬性 items 中包含的元素)。

print([item for item in grocery])
[Item('Milk (1L)', 0.99), Item('Soy Sauce (0.375L)', 1.99)]

同樣,訪問元素也是通過定義 __getitem__ 函數(shù)來處理的:

>>> grocery[new_item]
1

fake_item = Item("Creamy Cheese (500g)", 2.99)
>>> grocery[fake_item]
KeyError: "Item Item('Creamy Cheese (500g)', 2.99) not in the grocery"

從本質(zhì)上講,我們?yōu)?Grocery 類分配了一些類似字典的標準行為,同時也允許進行一些該數(shù)據(jù)類型本機無法進行的操作。

增強功能:使類可調(diào)用,以實現(xiàn)簡單性和強大功能。

最后,讓我們用一個示例來結(jié)束對 dunder 方法的深入探討,展示它們?nèi)绾纬蔀槲覀兊膹姶蠊ぞ摺?/span>

想象一下,我們實現(xiàn)了一個函數(shù),它可以根據(jù)特定輸入執(zhí)行確定性的慢速計算。為了簡單起見,我們將以一個內(nèi)置 time.sleep 為幾秒的標識函數(shù)為例。

import time 

def expensive_function(input):
    time.sleep(5)
    return input

如果我們對同一輸入運行兩次函數(shù),會發(fā)生什么情況?那么,現(xiàn)在計算將被執(zhí)行兩次,這意味著我們將兩次獲得相同的輸出,在整個執(zhí)行時間內(nèi)等待兩次(即總共 10 秒)。

start_time = time.time()

>>> print(expensive_function(2))
>>> print(expensive_function(2))
>>> print(f"Time for computation: {round(time.time()-start_time, 1)} seconds")
2
2
Time for computation: 10.0 seconds

這合理嗎?為什么我們要對相同的輸入進行相同的計算(導(dǎo)致相同的輸出),尤其是在計算過程很慢的情況下?

一種可能的解決方案是將該函數(shù)的執(zhí)行 “封裝 ”在類的 __call__ dunder 方法中。

這使得類的實例可以像函數(shù)一樣被調(diào)用--這意味著我們可以使用簡單的語法 my_class_instance(\*args,\**kwargs) --同時也允許我們使用屬性作為緩存來減少計算時間。

通過這種方法,我們還可以靈活地創(chuàng)建多個進程(即類實例),每個進程都有自己的本地緩存。

class CachedExpensiveFunction:

    def __init__(self) -> None:
        self.cache = dict()

    def __call__(self, input):
        if input not in self.cache:
            output = expensive_function(input=input)
            self.cache[input] = output
            return output
        else:
            return self.cache.get(input)


start_time = time.time()
cached_exp_func = CachedExpensiveFunction()

>>> print(cached_exp_func(2))
>>> print(cached_exp_func(2))
>>> print(f"Time for computation: {round(time.time()-start_time, 1)} seconds")
2
2
Time for computation: 5.0 seconds

不出所料,函數(shù)在第一次運行后會被緩存起來,這樣就不需要進行第二次計算,從而將總時間縮短了一半。

如上所述,如果需要,我們甚至可以創(chuàng)建該類的獨立實例,每個實例都有自己的緩存。

start_time = time.time()
another_cached_exp_func = CachedExpensiveFunction()

>>> print(cached_exp_func(3))
>>> print(another_cached_exp_func (3))
>>> print(f"Time for computation: {round(time.time()-start_time, 1)} seconds")
3
3
Time for computation: 10.0 seconds

dunder 方法是一個簡單而強大的優(yōu)化技巧,它不僅可以減少冗余計算,還可以通過本地特定實例緩存提供靈活性。

寫在最后

Dunder方法(就是那些用雙下劃線__包裹的特殊方法)在Python中是個很大的話題,而且還在不斷豐富。這篇文章當然沒法面面俱到地講完所有內(nèi)容。

我寫這些主要是想幫你弄明白兩件事:

  1. Dunder方法到底是什么?
  2. 怎么用它們解決實際編程中常見的問題?

說實話,不是每個程序員都必須掌握這些方法。但就我個人經(jīng)驗來說,當我真正搞懂它們之后,寫代碼的效率提高了很多。相信對你也會很有幫助。

使用Dunder方法最大的好處就是:

  • 不用重復(fù)造輪子
  • 讓代碼更簡潔易讀
  • 更符合Python的編程風格
責任編輯:武曉燕 來源: 數(shù)據(jù)STUDIO
相關(guān)推薦

2023-11-29 18:11:17

Python代碼

2020-09-22 09:41:48

Python下劃線開發(fā)

2023-11-09 08:55:17

Python雙下劃線

2010-03-04 10:35:51

Python下劃線

2021-08-08 22:27:13

Python下劃線方法

2024-08-20 16:32:37

python接口自動化

2021-12-26 22:55:26

Python下劃線編程

2021-07-31 19:21:34

Python下劃線數(shù)值

2024-08-16 22:00:38

2013-07-24 19:27:45

iOS開發(fā)學習自定義帶下劃線文本UI

2020-10-24 11:09:35

Python下劃線用法

2014-05-08 10:36:59

CSS單詞連字符

2021-10-20 07:36:03

Python構(gòu)造方法

2010-01-20 14:13:33

C++變量

2022-01-27 22:50:01

鏈表雙指針結(jié)構(gòu)

2022-12-19 15:12:34

python運算符

2023-11-30 08:21:33

2024-09-26 10:44:53

MySQL數(shù)據(jù)庫數(shù)據(jù)

2015-07-22 17:21:34

Oracle數(shù)據(jù)字典

2009-07-10 14:58:13

JLabel組件JFC和Swing
點贊
收藏

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

主站蜘蛛池模板: 成人性视频在线播放 | 一级黄色片一级黄色片 | 成人精品影院 | 日韩视频一区二区三区 | 亚洲精品中文字幕在线 | 桃色五月 | 91精品无人区卡一卡二卡三 | 精品国产乱码久久久久久影片 | 夜夜爽99久久国产综合精品女不卡 | 亚洲欧美中文日韩在线v日本 | 99热播精品 | 中文字幕第49页 | 久久久av| 91九色视频 | 99爱在线免费观看 | 亚洲国产精品99久久久久久久久 | 91九色porny首页最多播放 | 国产ts人妖系列高潮 | 久久久久久国产精品免费免费男同 | 日本成人一区二区 | 91精品国产自产在线老师啪 | 91国内精品久久 | 日韩视频一级 | 久操伊人| av在线免费网站 | 国产成人一区二区三区电影 | 日日夜夜精品视频 | 免费毛片网| 免费视频二区 | 国产精品视频久久久 | 久久9视频 | 老司机成人在线 | 91精品中文字幕一区二区三区 | 久久国内精品 | 91精品国产综合久久精品 | 超碰人人艹 | 亚洲综合在线播放 | 天天干天天干 | 亚洲人精品 | 99久久婷婷国产综合精品电影 | 成人黄色在线 |