使用 namedtuple 編寫 Pythonic 和干凈的代碼 |【生長(zhǎng)吧!Python!】 【生長(zhǎng)吧!Python】有獎(jiǎng)?wù)魑幕馃徇M(jìn)行中:https://bbs.huaweicloud.com/blogs/278897

      網(wǎng)友投稿 710 2025-03-31

      目錄

      使用 namedtuple 編寫 Pythonic 代碼

      使用 namedtuple() 創(chuàng)建類似元組的類

      為 namedtuple() 提供必需的參數(shù)

      在 namedtuple() 中使用可選參數(shù)

      探索 namedtuple 類的附加功能

      從可迭代對(duì)象創(chuàng)建命名元組實(shí)例

      將命名元組實(shí)例轉(zhuǎn)換為字典

      替換現(xiàn)有命名元組實(shí)例中的字段

      探索額外的命名元組屬性

      使用 namedtuple 編寫 Pythonic 代碼

      使用字段名稱而不是索引

      從函數(shù)返回多個(gè)命名值

      減少函數(shù)的參數(shù)數(shù)量

      從文件和數(shù)據(jù)庫(kù)中讀取表格數(shù)據(jù)

      使用命名元組與其他數(shù)據(jù)結(jié)構(gòu)

      命名元組與字典

      命名元組與數(shù)據(jù)類

      namedtuple 與打字.NamedTuple

      子類化 namedtuple 類

      測(cè)量創(chuàng)建時(shí)間:元組與命名元組

      結(jié)論

      Python 的collections模塊提供了一個(gè)名為的工廠函數(shù)namedtuple(),它專門設(shè)計(jì)用于在您使用元組時(shí)使您的代碼更加Pythonic。使用namedtuple(),您可以創(chuàng)建不可變的序列類型,允許您使用描述性字段名稱和點(diǎn)表示法而不是不明確的整數(shù)索引來訪問它們的值。

      如果您有使用 Python 的經(jīng)驗(yàn),那么您就會(huì)知道編寫 Pythonic 代碼是 Python 開發(fā)人員的核心技能。在本教程中,您將使用namedtuple.

      在本教程中,您將學(xué)習(xí)如何:

      namedtuple使用創(chuàng)建類namedtuple()

      識(shí)別并利用很酷的功能的namedtuple

      使用namedtuple實(shí)例編寫Pythonic 代碼

      決定是否使用 namedtuple或類似的數(shù)據(jù)結(jié)構(gòu)

      子類namedtuple提供新功能

      為了充分利用本教程,您需要對(duì) Python 與編寫 Pythonic 和可讀代碼相關(guān)的哲學(xué)有一個(gè)大致的了解。您還需要了解與以下人員合作的基礎(chǔ)知識(shí):

      元組

      字典

      類和面向?qū)ο缶幊?/a>

      數(shù)據(jù)類

      輸入提示

      如果您在開始本教程之前沒有掌握所有必需的知識(shí),那也沒關(guān)系!您可以根據(jù)需要停止并查看上述資源。

      使用namedtuple編寫Python的代碼

      Pythonnamedtuple()是一個(gè)工廠函數(shù),可在collections.?它允許您創(chuàng)建tuple具有命名字段的子類。您可以使用點(diǎn)表示法和字段名稱訪問給定命名元組中的值,例如 in?obj.attr。

      Python 的namedtuple創(chuàng)建是為了通過提供一種使用描述性字段名稱而不是整數(shù)索引來訪問值的方法來提高代碼可讀性,大多數(shù)情況下,整數(shù)索引不提供有關(guān)值的任何上下文。此功能還使代碼更簡(jiǎn)潔,更易于維護(hù)。

      相比之下,對(duì)常規(guī)元組中的值使用索引可能很煩人、難以閱讀且容易出錯(cuò)。如果元組有很多字段并且構(gòu)建的位置遠(yuǎn)離您使用它的地方,則尤其如此。

      注意:在本教程中,您會(huì)發(fā)現(xiàn)用于指代 Python 的namedtuple、其工廠函數(shù)及其實(shí)例的不同術(shù)語。

      為避免混淆,這里總結(jié)了整個(gè)教程中每個(gè)術(shù)語的使用方式:

      在整個(gè)教程中,您會(huì)發(fā)現(xiàn)這些術(shù)語的使用及其相應(yīng)的含義。

      除了命名元組的這一主要功能外,您還會(huì)發(fā)現(xiàn)它們:

      是不可變的數(shù)據(jù)結(jié)構(gòu)

      具有一致的哈希值

      可以用作字典鍵

      可以成套存儲(chǔ)

      有一個(gè)基于類型和字段名稱的有用文檔字符串

      提供有用的字符串表示形式,以某種name=value格式打印元組內(nèi)容

      支持索引

      提供其他方法和屬性,例如._make()、_asdict()、._fields等

      是向后兼容與普通的元組

      具有與常規(guī)元組相似的內(nèi)存消耗

      通常,您可以namedtuple在需要類似元組的對(duì)象的任何地方使用實(shí)例。命名元組的優(yōu)勢(shì)在于它們提供了一種使用字段名稱和點(diǎn)表示法訪問其值的方法。這將使您的代碼更加 Pythonic。

      通過namedtuple對(duì)它的簡(jiǎn)要介紹及其一般特性,您可以更深入地了解如何在代碼中創(chuàng)建和使用它們。

      創(chuàng)建類似元組的類?namedtuple()

      您可以使用 anamedtuple()創(chuàng)建具有字段名稱的不可變且類似于元組的數(shù)據(jù)結(jié)構(gòu)。您可以在教程中找到的一個(gè)流行示例namedtuple是創(chuàng)建一個(gè)類來表示一個(gè)數(shù)學(xué)點(diǎn)。

      根據(jù)問題的不同,您可能希望使用不可變的數(shù)據(jù)結(jié)構(gòu)來表示給定的點(diǎn)。以下是使用常規(guī)元組創(chuàng)建二維點(diǎn)的方法:

      >>>

      >>> # Create a 2D point as a tuple >>> point = (2, 4) >>> point (2, 4) >>> # Access coordinate x >>> point[0] 2 >>> # Access coordinate y >>> point[1] 4 >>> # Try to update a coordinate value >>> point[0] = 3 Traceback (most recent call last): File "", line 1, in TypeError: 'tuple' object does not support item assignment

      在這里,您point使用常規(guī)tuple.?此代碼有效:您有point兩個(gè)坐標(biāo),并且不能修改任何這些坐標(biāo)。但是,這段代碼可讀嗎?你能預(yù)先說出0和1指數(shù)的含義嗎?為了防止這些歧義,你可以使用namedtuple這樣的:

      >>>

      >>> from collections import namedtuple >>> # Create a namedtuple type, Point >>> Point = namedtuple("Point", "x y") >>> issubclass(Point, tuple) True >>> # Instantiate the new type >>> point = Point(2, 4) >>> point Point(x=2, y=4) >>> # Dot notation to access coordinates >>> point.x 2 >>> point.y 4 >>> # Indexing to access coordinates >>> point[0] 2 >>> point[1] 4 >>> # Named tuples are immutable >>> point.x = 100 Traceback (most recent call last): File "", line 1, in AttributeError: can't set attribute

      現(xiàn)在你有point兩個(gè)適當(dāng)命名的字段,x和y.?默認(rèn)情況下,您point提供了用戶友好且描述性的字符串表示形式 (?Point(x=2, y=4))。它允許您使用點(diǎn)表示法訪問坐標(biāo),該表示法方便、易讀且明確。您還可以使用索引來訪問每個(gè)坐標(biāo)的值。

      注意:重要的是要注意,雖然元組和命名元組是不可變的,但它們存儲(chǔ)的值不一定是不可變的。

      創(chuàng)建包含可變值的元組或命名元組是完全合法的:

      >>>

      >>> from collections import namedtuple >>> Person = namedtuple("Person", "name children") >>> john = Person("John Doe", ["Timmy", "Jimmy"]) >>> john Person(name='John Doe', children=['Timmy', 'Jimmy']) >>> id(john.children) 139695902374144 >>> john.children.append("Tina") >>> john Person(name='John Doe', children=['Timmy', 'Jimmy', 'Tina']) >>> id(john.children) 139695902374144 >>> hash(john) Traceback (most recent call last): File "", line 1, in TypeError: unhashable type: 'list'

      您可以創(chuàng)建包含可變對(duì)象的命名元組。您可以修改底層元組中的可變對(duì)象。但是,這并不意味著您正在修改元組本身。元組將繼續(xù)持有相同的內(nèi)存引用。

      最后,具有可變值的元組或命名元組不是hashable,如您在上面的示例中看到的。

      最后,由于namedtuple類是 的子類tuple,它們也是不可變的。因此,如果您嘗試更改坐標(biāo)的值,則會(huì)得到AttributeError.

      提供所需的參數(shù)?namedtuple()

      正如您之前了解到的,namedtuple()是工廠函數(shù)而不是典型的數(shù)據(jù)結(jié)構(gòu)。要?jiǎng)?chuàng)建 new?namedtuple,您需要為函數(shù)提供兩個(gè)位置參數(shù):

      typenamenamedtuple為由 返回的提供類名namedtuple()。您需要將帶有有效 Python 標(biāo)識(shí)符的字符串傳遞給該參數(shù)。

      field_names提供您將用于訪問元組中的值的字段名稱。您可以使用以下方法提供字段名稱:

      一個(gè)可迭代的字符串,例如["field1", "field2", ..., "fieldN"]

      每個(gè)字段名稱以空格分隔的字符串,例如?"field1 field2 ... fieldN"

      每個(gè)字段名稱以逗號(hào)分隔的字符串,例如?"field1, field2, ..., fieldN"

      為了說明如何提供field_names,以下是創(chuàng)建點(diǎn)的不同方法:

      >>>

      >>> from collections import namedtuple >>> # A list of strings for the field names >>> Point = namedtuple("Point", ["x", "y"]) >>> Point >>> Point(2, 4) Point(x=2, y=4) >>> # A string with comma-separated field names >>> Point = namedtuple("Point", "x, y") >>> Point >>> Point(4, 8) Point(x=4, y=8) >>> # A generator expression for the field names >>> Point = namedtuple("Point", (field for field in "xy")) >>> Point >>> Point(8, 16) Point(x=8, y=16)

      在這些示例中,您首先Point使用list字段名稱創(chuàng)建。然后您使用帶有以逗號(hào)分隔的字段名稱的字符串。最后,您使用生成器表達(dá)式。在這個(gè)例子中,最后一個(gè)選項(xiàng)可能看起來有點(diǎn)矯枉過正。但是,它旨在說明該過程的靈活性。

      注意:如果您使用可迭代對(duì)象來提供字段名稱,那么您應(yīng)該使用類似序列的可迭代對(duì)象,因?yàn)樽侄蔚捻樞驅(qū)τ诋a(chǎn)生可靠的結(jié)果很重要。

      set例如,使用 a可以工作,但可能會(huì)產(chǎn)生意想不到的結(jié)果:

      >>>

      >>> from collections import namedtuple >>> Point = namedtuple("Point", {"x", "y"}) >>> Point(2, 4) Point(y=2, x=4)

      當(dāng)您使用無序可迭代對(duì)象向 a 提供字段時(shí)namedtuple,您可能會(huì)得到意想不到的結(jié)果。在上面的示例中,坐標(biāo)名稱被交換,這可能不適合您的用例。

      您可以對(duì)字段名稱使用任何有效的 Python 標(biāo)識(shí)符,但以下情況除外:

      以下劃線 (?_)開頭的名稱

      Python?keywords

      如果您提供違反這些條件之一的字段名稱,則您會(huì)得到ValueError:

      >>>

      >>> from collections import namedtuple >>> Point = namedtuple("Point", ["x", "_y"]) Traceback (most recent call last): ... ValueError: Field names cannot start with an underscore: '_y'

      在此示例中,第二個(gè)字段名稱以和下劃線開頭,因此您會(huì)收到ValueError通知,字段名稱不能以該字符開頭。這是為了避免與namedtuple方法和屬性的名稱沖突。

      在 的情況下typename,當(dāng)您查看上面的示例時(shí)可能會(huì)出現(xiàn)一個(gè)問題:為什么我需要提供typename參數(shù)?答案是您需要為由 返回的類命名namedtuple()。這就像為現(xiàn)有類創(chuàng)建別名:

      >>>

      >>> from collections import namedtuple >>> Point1 = namedtuple("Point", "x y") >>> Point1 >>> class Point: ... def __init__(self, x, y): ... self.x = x ... self.y = y ... >>> Point2 = Point >>> Point2

      在第一個(gè)示例中,您Point使用namedtuple().?然后將這個(gè)新類型分配給全局?變量?Point1。在第二個(gè)示例中,您創(chuàng)建一個(gè)也名為 的常規(guī) Python 類Point,然后將該類分配給Point2。在這兩種情況下,類名都是Point.?Point1和Point2是手頭類的別名。

      最后,您還可以使用關(guān)鍵字參數(shù)或提供現(xiàn)有字典來創(chuàng)建命名元組,如下所示:

      >>>

      >>> from collections import namedtuple >>> Point = namedtuple("Point", "x y") >>> Point(x=2, y=4) Point(x=2, y=4) >>> Point(**{"x": 4, "y": 8}) Point(x=4, y=8)

      在第一個(gè)示例中,您使用關(guān)鍵字參數(shù)來創(chuàng)建一個(gè)Point對(duì)象。在第二個(gè)示例中,您使用的字典的鍵與 的字段匹配Point。在這種情況下,您需要執(zhí)行字典解包。

      使用可選參數(shù)?namedtuple()

      除了兩個(gè)必需的參數(shù)外,namedtuple()工廠函數(shù)還采用以下可選參數(shù):

      rename

      defaults

      module

      如果設(shè)置rename為True,則所有無效字段名稱都會(huì)自動(dòng)替換為位置名稱。

      假設(shè)您的公司有一個(gè)用 Python 編寫的舊數(shù)據(jù)庫(kù)應(yīng)用程序,用于管理與公司一起旅行的乘客的數(shù)據(jù)。你被要求更新系統(tǒng),然后你開始創(chuàng)建命名元組來存儲(chǔ)你從數(shù)據(jù)庫(kù)中讀取的數(shù)據(jù)。

      該應(yīng)用程序提供了一個(gè)名為的函數(shù)get_column_names(),該函數(shù)返回帶有列名的字符串列表,您認(rèn)為可以使用該函數(shù)創(chuàng)建一個(gè)namedtuple類。你最終得到以下代碼:

      # passenger.py from collections import namedtuple from database import get_column_names Passenger = namedtuple("Passenger", get_column_names())

      但是,當(dāng)您運(yùn)行代碼時(shí),您會(huì)得到如下所示的異常回溯:

      Traceback (most recent call last): ... ValueError: Type names and field names cannot be a keyword: 'class'

      這告訴您class列名稱不是您的namedtuple類的有效字段名稱。為了防止這種情況,您決定使用rename:

      # passenger.py # ... Passenger = namedtuple("Passenger", get_column_names(), rename=True)

      這會(huì)導(dǎo)致namedtuple()自動(dòng)用位置名稱替換無效名稱。現(xiàn)在假設(shè)您從數(shù)據(jù)庫(kù)中檢索一行并創(chuàng)建您的第一個(gè)Passenger實(shí)例,如下所示:

      >>>

      >>> from passenger import Passenger >>> from database import get_passenger_by_id >>> Passenger(get_passenger_by_id("1234")) Passenger(_0=1234, name='john', _2='Business', _3='John Doe')

      在這種情況下,get_passenger_by_id()是您假設(shè)的應(yīng)用程序中可用的另一個(gè)函數(shù)。它檢索元組中給定乘客的數(shù)據(jù)。最終的結(jié)果是你新創(chuàng)建的passenger有三個(gè)位置字段名,并且只name反映了原來的列名。當(dāng)您深入數(shù)據(jù)庫(kù)時(shí) ,您會(huì)發(fā)現(xiàn)乘客表具有以下列:

      在基于您控制之外的值創(chuàng)建命名元組的情況下,該rename選項(xiàng)應(yīng)設(shè)置為,True以便使用有效的位置名稱重命名無效字段。

      的第二個(gè)可選參數(shù)namedtuple()是defaults。此參數(shù)默認(rèn)為None,這意味著字段將沒有默認(rèn)值。您可以設(shè)置defaults為可迭代的值。在這種情況下,namedtuple()將defaults迭代中的值分配給最右邊的字段:

      >>>

      >>> from collections import namedtuple >>> Developer = namedtuple( ... "Developer", ... "name level language", ... defaults=["Junior", "Python"] ... ) >>> Developer("John") Developer(name='John', level='Junior', language='Python')

      在此示例中,level和language字段具有默認(rèn)值。這使它們成為可選參數(shù)。由于您沒有為 定義默認(rèn)值name,因此您需要在創(chuàng)建namedtuple實(shí)例時(shí)提供一個(gè)值。因此,需要沒有默認(rèn)值的參數(shù)。請(qǐng)注意,默認(rèn)值應(yīng)用于最右側(cè)的字段。

      的最后一個(gè)參數(shù)namedtuple()是module。如果為此參數(shù)提供有效的模塊名稱,則結(jié)果的.__module__屬性將namedtuple設(shè)置為該值。此屬性保存定義給定函數(shù)或可調(diào)用對(duì)象的模塊的名稱:

      >>>

      >>> from collections import namedtuple >>> Point = namedtuple("Point", "x y", module="custom") >>> Point >>> Point.__module__ 'custom'

      在此示例中,當(dāng)您訪問.__module__on 時(shí)Point,您會(huì)得到'custom'結(jié)果。這表明您的Point類是在您的custom模塊中定義的。

      在Python 3.6 中添加參數(shù)的動(dòng)機(jī)是使命名元組能夠通過不同的Python 實(shí)現(xiàn)支持酸洗。modulenamedtuple()

      探索namedtuple類的附加功能

      除了從繼承的方法tuple,如.count()和.index(),namedtuple類還提供三種額外的方法和兩個(gè)屬性。為了防止與自定義字段的名稱沖突,這些屬性和方法的名稱以下劃線開頭。在本節(jié)中,您將了解這些方法和屬性以及它們的工作原理。

      namedtuple從可迭代對(duì)象創(chuàng)建實(shí)例

      您可以使用._make()來創(chuàng)建命名元組實(shí)例。該方法接受一個(gè)可迭代的值并返回一個(gè)新的命名元組:

      >>>

      >>> from collections import namedtuple >>> Person = namedtuple("Person", "name age height") >>> Person._make(["Jane", 25, 1.75]) Person(name='Jane', age=25, height=1.75)

      在這里,您首先Person使用namedtuple().?然后,您._make()使用 .csv 文件中每個(gè)字段的值列表進(jìn)行調(diào)用namedtuple。請(qǐng)注意,這._make()是一個(gè)用作替代類構(gòu)造函數(shù)并返回一個(gè)新命名元組實(shí)例的類方法。

      最后,._make()期望一個(gè)可迭代對(duì)象作為參數(shù),list在上面的例子中是 a 。另一方面,namedtuple構(gòu)造函數(shù)可以采用位置參數(shù)或關(guān)鍵字參數(shù),正如您已經(jīng)了解的那樣。

      將namedtuple實(shí)例轉(zhuǎn)換為字典

      您可以使用 將現(xiàn)有的命名元組實(shí)例轉(zhuǎn)換為字典._asdict()。此方法返回一個(gè)使用字段名稱作為鍵的新字典。結(jié)果字典的鍵與原始字典中的字段順序相同namedtuple:

      >>>

      >>> from collections import namedtuple >>> Person = namedtuple("Person", "name age height") >>> jane = Person("Jane", 25, 1.75) >>> jane._asdict() {'name': 'Jane', 'age': 25, 'height': 1.75}

      當(dāng)您調(diào)用._asdict()命名元組時(shí),您將獲得一個(gè)新dict對(duì)象,該對(duì)象將字段名稱映射到原始命名元組中的相應(yīng)值。

      從Python 3.8 開始,._asdict()返回了一個(gè)普通字典。在此之前,它返回了一個(gè)OrderedDict對(duì)象:

      >>>

      Python 3.7.9 (default, Jan 14 2021, 11:41:20) [GCC 9.3.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from collections import namedtuple >>> Person = namedtuple("Person", "name age height") >>> jane = Person("Jane", 25, 1.75) >>> jane._asdict() OrderedDict([('name', 'Jane'), ('age', 25), ('height', 1.75)])

      Python 3.8 更新._asdict()為返回常規(guī)字典,因?yàn)樽值鋾?huì)記住其鍵在 Python 3.6 及更高版本中的插入順序。請(qǐng)注意,結(jié)果字典中鍵的順序等同于原始命名元組中字段的順序。

      替換現(xiàn)有namedtuple實(shí)例中的字段

      您將學(xué)習(xí)的最后一種方法是._replace().?此方法采用表單的關(guān)鍵字參數(shù)field=value并返回一個(gè)namedtuple更新所選字段值的新實(shí)例:

      >>>

      >>> from collections import namedtuple >>> Person = namedtuple("Person", "name age height") >>> jane = Person("Jane", 25, 1.75) >>> # After Jane's birthday >>> jane = jane._replace(age=26) >>> jane Person(name='Jane', age=26, height=1.75)

      在此示例中,您在 Jane 生日后更新她的年齡。盡管 的名稱._replace()可能暗示該方法修改了現(xiàn)有的命名元組,但實(shí)際情況并非如此。這是因?yàn)閚amedtuple實(shí)例是不可變的,所以._replace()不會(huì)jane?就地更新。

      探索附加namedtuple屬性

      命名元組還有兩個(gè)額外的屬性:._fields和._field_defaults.?第一個(gè)屬性包含一個(gè)列出字段名稱的字符串元組。第二個(gè)屬性包含一個(gè)字典,該字典將字段名稱映射到它們各自的默認(rèn)值(如果有)。

      在 的情況下._fields,您可以使用它來內(nèi)省您的namedtuple類和實(shí)例。您還可以從現(xiàn)有類創(chuàng)建新類:

      >>>

      >>> from collections import namedtuple >>> Person = namedtuple("Person", "name age height") >>> ExtendedPerson = namedtuple( ... "ExtendedPerson", ... [*Person._fields, "weight"] ... ) >>> jane = ExtendedPerson("Jane", 26, 1.75, 67) >>> jane ExtendedPerson(name='Jane', age=26, height=1.75, weight=67) >>> jane.weight 67

      在此示例中,您將創(chuàng)建一個(gè)具有新字段的新namedtuple調(diào)用ExtendedPerson,weight。這種新類型擴(kuò)展了您的舊Person.?為此,您可以訪問._fieldsonPerson并將其解壓縮到一個(gè)新列表以及一個(gè)附加字段weight.

      您還可以使用 Python._fields來迭代給定namedtuple實(shí)例中的字段和值z(mì)ip():

      >>>

      >>> from collections import namedtuple >>> Person = namedtuple("Person", "name age height weight") >>> jane = Person("Jane", 26, 1.75, 67) >>> for field, value in zip(jane._fields, jane): ... print(field, "->", value) ... name -> Jane age -> 26 height -> 1.75 weight -> 67

      在這個(gè)例子中,zip()產(chǎn)生形式為 的元組(field, value)。這樣,您就可以訪問底層命名元組中的字段-值對(duì)的兩個(gè)元素。另一種同時(shí)迭代字段和值的方法是使用._asdict().items().?來試試看吧!

      使用._field_defaults,您可以內(nèi)省namedtuple類和實(shí)例以找出哪些字段提供默認(rèn)值。具有默認(rèn)值使您的字段可選。例如,假設(shè)您的Person類應(yīng)該包含一個(gè)額外的字段來保存此人居住的國(guó)家/地區(qū)。由于您主要與來自加拿大的人一起工作,因此您可以為該country字段設(shè)置適當(dāng)?shù)哪J(rèn)值,如下所示:

      >>>

      >>> from collections import namedtuple >>> Person = namedtuple( ... "Person", ... "name age height weight country", ... defaults=["Canada"] ... ) >>> Person._field_defaults {'country': 'Canada'}

      通過對(duì) 的快速查詢._field_defaults,您可以找出給定的哪些字段namedtuple提供默認(rèn)值。在此示例中,您團(tuán)隊(duì)中的任何其他程序員都可以看到您的Person類"Canada"為country.

      如果您namedtuple不提供默認(rèn)值,則.field_defaults保存一個(gè)空字典:

      >>>

      >>> from collections import namedtuple >>> Person = namedtuple("Person", "name age height weight country") >>> Person._field_defaults {}

      如果您沒有向 提供默認(rèn)值列表namedtuple(),則它依賴于 的默認(rèn)值defaults,即None。在這種情況下,._field_defaults持有一個(gè)空字典。

      編寫 Pythonic 代碼?namedtuple

      可以說,命名元組的基本用例是幫助您編寫更多 Pythonic 代碼。該namedtuple()工廠的功能是為了讓你寫可讀的,明確的,干凈,和維護(hù)的代碼。

      在本節(jié)中,您將編寫大量實(shí)際示例,這些示例將幫助您發(fā)現(xiàn)使用命名元組而不是常規(guī)元組的好機(jī)會(huì),從而使您的代碼更加 Pythonic。

      使用字段名稱而不是索引

      假設(shè)您正在創(chuàng)建一個(gè)繪畫應(yīng)用程序,并且您需要根據(jù)用戶的選擇定義要使用的筆屬性。您已經(jīng)在元組中對(duì)筆的屬性進(jìn)行了編碼:

      >>>

      >>> pen = (2, "Solid", True) >>> if pen[0] == 2 and pen[1] == "Solid" and pen[2]: ... print("Standard pen selected") ... Standard pen selected

      這行代碼定義了一個(gè)具有三個(gè)值的元組。你能說出每個(gè)值的含義嗎?也許你能猜到第二個(gè)值和線型有關(guān),但是2and是什么意思True呢?

      您可以添加一個(gè)很好的注釋來為 提供一些上下文pen,在這種情況下,您最終會(huì)得到如下內(nèi)容:

      >>>

      >>> # Tuple containing: line weight, line style, and beveled edges >>> pen = (2, "Solid", True)

      涼爽的!現(xiàn)在您知道元組中每個(gè)值的含義。但是,如果您或其他程序員使用的pen遠(yuǎn)離此定義怎么辦?他們必須回到定義來記住每個(gè)值的含義。

      這是pen使用 a的另一種實(shí)現(xiàn)namedtuple:

      >>>

      >>> from collections import namedtuple >>> Pen = namedtuple("Pen", "width style beveled") >>> pen = Pen(2, "Solid", True) >>> if pen.width == 2 and pen.style == "Solid" and pen.beveled: ... print("Standard pen selected") ... Standard pen selected

      現(xiàn)在,您的代碼清楚地表明2表示筆的寬度、"Solid"線條樣式等。任何閱讀您代碼的人都可以看到并理解這一點(diǎn)。您的新實(shí)現(xiàn)pen有兩行額外的代碼。這是在可讀性和可維護(hù)性方面取得巨大勝利的少量工作。

      從函數(shù)返回多個(gè)命名值

      可以使用命名元組的另一種情況是需要從給定函數(shù)返回多個(gè)值。在這種情況下,使用命名元組可以使您的代碼更具可讀性,因?yàn)榉祷氐闹颠€將為其內(nèi)容提供一些上下文。

      例如,Python 提供了一個(gè)內(nèi)置函數(shù)divmod(),該函數(shù)接受兩個(gè)數(shù)字作為參數(shù),并返回一個(gè)元組,其中包含輸入數(shù)字的整數(shù)除法的商和余數(shù):

      >>>

      >>> divmod(8, 4) (2, 0)

      要記住每個(gè)數(shù)字的含義,您可能需要閱讀 的文檔,divmod()因?yàn)閿?shù)字本身并沒有提供有關(guān)其各自含義的太多信息。該函數(shù)的名稱也沒有太大幫助。

      這是一個(gè)函數(shù),它使用 anamedtuple來闡明每個(gè)divmod()返回?cái)?shù)字的含義:

      >>>

      >>> from collections import namedtuple >>> def custom_divmod(a, b): ... DivMod = namedtuple("DivMod", "quotient remainder") ... return DivMod(*divmod(a, b)) ... >>> custom_divmod(8, 4) DivMod(quotient=2, remainder=0)

      在此示例中,您為每個(gè)返回值添加了上下文,因此任何閱讀您代碼的程序員都可以立即理解每個(gè)數(shù)字的含義。

      減少函數(shù)的參數(shù)數(shù)量

      減少函數(shù)可以采用的參數(shù)數(shù)量被認(rèn)為是最佳編程實(shí)踐。這使您的函數(shù)簽名更加簡(jiǎn)潔并優(yōu)化您的測(cè)試過程,因?yàn)閰?shù)數(shù)量和它們之間可能的組合減少了。

      同樣,您應(yīng)該考慮使用命名元組來處理這個(gè)用例。假設(shè)您正在編寫一個(gè)應(yīng)用程序來管理客戶的信息。該應(yīng)用程序使用數(shù)據(jù)庫(kù)來存儲(chǔ)客戶的數(shù)據(jù)。為了處理數(shù)據(jù)和更新數(shù)據(jù)庫(kù),您已經(jīng)創(chuàng)建了幾個(gè)函數(shù)。您的高級(jí)函數(shù)之一是create_user(),如下所示:

      def create_user(db, username, client_name, plan): db.add_user(username) db.complete_user_profile(username, client_name, plan)

      這個(gè)函數(shù)有四個(gè)參數(shù)。第一個(gè)參數(shù) ,db代表您正在使用的數(shù)據(jù)庫(kù)。其余的參數(shù)與給定的客戶密切相關(guān)。這是減少create_user()使用命名元組的參數(shù)數(shù)量的好機(jī)會(huì):

      User = namedtuple("User", "username client_name plan") user = User("john", "John Doe", "Premium") def create_user(db, user): db.add_user(user.username) db.complete_user_profile( user.username, user.client_name, user.plan )

      現(xiàn)在create_user()只需要兩個(gè)參數(shù):db和user。在函數(shù)內(nèi)部,您使用方便且具有描述性的字段名稱來為db.add_user()和提供參數(shù)db.complete_user_profile()。您的高級(jí)函數(shù)create_user()更側(cè)重于user.?測(cè)試也更容易,因?yàn)槟恍枰獮槊總€(gè)測(cè)試提供兩個(gè)參數(shù)。

      從文件和數(shù)據(jù)庫(kù)中讀取表格數(shù)據(jù)

      命名元組的一個(gè)非常常見的用例是使用它們來存儲(chǔ)數(shù)據(jù)庫(kù)記錄。您可以namedtuple使用列名作為字段名來定義類,并將數(shù)據(jù)從數(shù)據(jù)庫(kù)中的行檢索到命名元組。您還可以對(duì)CSV 文件執(zhí)行類似操作。

      例如,假設(shè)您有一個(gè) CSV 文件,其中包含有關(guān)貴公司員工的數(shù)據(jù),并且您希望將該數(shù)據(jù)讀入合適的數(shù)據(jù)結(jié)構(gòu)以進(jìn)行進(jìn)一步處理。您的 CSV 文件如下所示:

      name,job,email "Linda","Technical Lead","linda@example.com" "Joe","Senior Web Developer","joe@example.com" "Lara","Project Manager","lara@example.com" "David","Data Analyst","david@example.com" "Jane","Senior Python Developer","jane@example.com"

      您正在考慮使用 Python 的csv模塊及其模塊DictReader來處理文件,但是您還有一個(gè)額外的要求——您需要將數(shù)據(jù)存儲(chǔ)到一個(gè)不可變的輕量級(jí)數(shù)據(jù)結(jié)構(gòu)中。在這種情況下, anamedtuple可能是一個(gè)不錯(cuò)的選擇:

      >>>

      >>> import csv >>> from collections import namedtuple >>> with open("employees.csv", "r") as csv_file: ... reader = csv.reader(csv_file) ... Employee = namedtuple("Employee", next(reader), rename=True) ... for row in reader: ... employee = Employee(*row) ... print(employee.name, employee.job, employee.email) ... Linda Technical Lead linda@example.com Joe Senior Web Developer joe@example.com Lara Project Manager lara@example.com David Data Analyst david@example.com Jane Senior Python Developer jane@example.com

      在本例中,您首先employees.csv在with語句中打開文件。然后使用csv.reader()CSV 文件中的行獲取迭代器。使用namedtuple(),您可以創(chuàng)建一個(gè)新Employee類。調(diào)用從next()檢索第一行數(shù)據(jù)reader,其中包含 CSV 文件標(biāo)題。此標(biāo)頭為您的namedtuple.

      注意:當(dāng)您創(chuàng)建一個(gè)namedtuple基于您無法控制的字段名稱時(shí),您應(yīng)該設(shè)置.rename為True.?這樣,您可以防止無效字段名稱的問題,這可能是您處理數(shù)據(jù)庫(kù)表和查詢 、CSV 文件或任何其他類型的表格數(shù)據(jù)時(shí)的常見情況。

      最后,for循環(huán)Employee從rowCSV 文件中的每個(gè)創(chuàng)建一個(gè)實(shí)例,并將員工列表打印到屏幕上。

      使用namedtuplevs 其他數(shù)據(jù)結(jié)構(gòu)

      到目前為止,您已經(jīng)學(xué)習(xí)了如何創(chuàng)建命名元組以使您的代碼更具可讀性、明確性和 Pythonic。您還編寫了一些示例,幫助您發(fā)現(xiàn)在代碼中使用命名元組的機(jī)會(huì)。

      在本節(jié)中,您將大致了解namedtuple類與其他 Python 數(shù)據(jù)結(jié)構(gòu)(例如字典、數(shù)據(jù)類和類型化命名元組)之間的異同。您將就以下特征將命名元組與其他數(shù)據(jù)結(jié)構(gòu)進(jìn)行比較:

      可讀性

      可變性

      內(nèi)存使用情況

      表現(xiàn)

      這樣,您將更好地準(zhǔn)備為您的特定用例選擇正確的數(shù)據(jù)結(jié)構(gòu)。

      namedtuple?VS 字典

      該字典是Python中的基本數(shù)據(jù)結(jié)構(gòu)。該語言本身是圍繞字典構(gòu)建的,因此它們無處不在。由于它們是如此常見和有用,您可能在代碼中經(jīng)常使用它們。但是字典和命名元組有什么不同呢?

      在可讀性方面,您可能會(huì)說字典與命名元組一樣可讀。盡管它們沒有提供通過點(diǎn)表示法訪問屬性的方法,但字典樣式的鍵查找非常易讀且簡(jiǎn)單:

      >>>

      >>> from collections import namedtuple >>> jane = {"name": "Jane", "age": 25, "height": 1.75} >>> jane["age"] 25 >>> # Equivalent named tuple >>> Person = namedtuple("Person", "name age height") >>> jane = Person("Jane", 25, 1.75) >>> jane.age 25

      在這兩個(gè)示例中,您都對(duì)代碼及其意圖有了全面的了解。命名元組定義需要的代碼兩條額外的線條,雖然:一行導(dǎo)入的namedtuple()工廠功能和另一個(gè)定義namedtuple類,Person。

      兩種數(shù)據(jù)結(jié)構(gòu)之間的一個(gè)很大區(qū)別是字典是可變的,而命名元組是不可變的。這意味著您可以就地修改字典,但不能修改命名元組:

      >>>

      >>> from collections import namedtuple >>> jane = {"name": "Jane", "age": 25, "height": 1.75} >>> jane["age"] = 26 >>> jane["age"] 26 >>> jane["weight"] = 67 >>> jane {'name': 'Jane', 'age': 26, 'height': 1.75, 'weight': 67} >>> # Equivalent named tuple >>> Person = namedtuple("Person", "name age height") >>> jane = Person("Jane", 25, 1.75) >>> jane.age = 26 Traceback (most recent call last): File "", line 1, in AttributeError: can't set attribute >>> jane.weight = 67 Traceback (most recent call last): File "", line 1, in AttributeError: 'Person' object has no attribute 'weight'

      您可以更新字典中現(xiàn)有鍵的值,但不能在命名元組中執(zhí)行類似操作。您可以向現(xiàn)有字典添加新的鍵值對(duì),但不能向現(xiàn)有命名元組添加字段值對(duì)。

      注意:在命名元組中,您可以使用._replace()來更新給定字段的值,但該方法會(huì)創(chuàng)建并返回一個(gè)新的命名元組實(shí)例,而不是就地更新底層實(shí)例。

      一般來說,如果您需要一個(gè)不可變的數(shù)據(jù)結(jié)構(gòu)來正確解決給定的問題,那么可以考慮使用命名元組而不是字典來滿足您的要求。

      關(guān)于內(nèi)存使用,命名元組是一種非常輕量級(jí)的數(shù)據(jù)結(jié)構(gòu)。啟動(dòng)您的代碼編輯器或 IDE并創(chuàng)建以下腳本:

      # namedtuple_dict_memory.py from collections import namedtuple from pympler import asizeof Point = namedtuple("Point", "x y z") point = Point(1, 2, 3) namedtuple_size = asizeof.asizeof(point) dict_size = asizeof.asizeof(point._asdict()) gain = 100 - namedtuple_size / dict_size * 100 print(f"namedtuple: {namedtuple_size} bytes ({gain:.2f}% smaller)") print(f"dict: {dict_size} bytes")

      這個(gè)小腳本使用asizeof.asizeof()來自Pympler來獲取命名元組及其等效字典的內(nèi)存占用。

      注意:?Pympler 是一個(gè)監(jiān)控和分析 Python 對(duì)象內(nèi)存行為的工具。

      您可以從安裝它的PyPI使用pip像往常一樣:

      $ pip install pympler

      運(yùn)行此命令后,Pympler 將在您的Python 環(huán)境中可用,因此您可以運(yùn)行上述腳本。

      如果從命令行運(yùn)行腳本,則會(huì)得到以下輸出:

      $ python namedtuple_dict_memory.py namedtuple: 160 bytes (67.74% smaller) dict: 496 bytes

      此輸出確認(rèn)命名元組比等效字典消耗更少的內(nèi)存。因此,如果內(nèi)存消耗對(duì)您來說是一個(gè)限制,那么您應(yīng)該考慮使用命名元組而不是字典。

      注意:當(dāng)您比較命名元組和字典時(shí),最終的內(nèi)存消耗差異將取決于值的數(shù)量及其類型。使用不同的值,您會(huì)得到不同的結(jié)果。

      最后,您需要了解命名元組和字典在操作性能方面的不同之處。為此,您將測(cè)試成員資格和屬性訪問操作。回到您的代碼編輯器并創(chuàng)建以下腳本:

      # namedtuple_dict_time.py from collections import namedtuple from time import perf_counter def average_time(structure, test_func): time_measurements = [] for _ in range(1_000_000): start = perf_counter() test_func(structure) end = perf_counter() time_measurements.append(end - start) return sum(time_measurements) / len(time_measurements) * int(1e9) def time_dict(dictionary): "x" in dictionary "missing_key" in dictionary 2 in dictionary.values() "missing_value" in dictionary.values() dictionary["y"] def time_namedtuple(named_tuple): "x" in named_tuple._fields "missing_field" in named_tuple._fields 2 in named_tuple "missing_value" in named_tuple named_tuple.y Point = namedtuple("Point", "x y z") point = Point(x=1, y=2, z=3) namedtuple_time = average_time(point, time_namedtuple) dict_time = average_time(point._asdict(), time_dict) gain = dict_time / namedtuple_time print(f"namedtuple: {namedtuple_time:.2f} ns ({gain:.2f}x faster)") print(f"dict: {dict_time:.2f} ns")

      使用 namedtuple 編寫 Pythonic 和干凈的代碼 |【生長(zhǎng)吧!Python!】 【生長(zhǎng)吧!Python】有獎(jiǎng)?wù)魑幕馃徇M(jìn)行中:https://bbs.huaweicloud.com/blogs/278897

      此腳本對(duì)字典和命名元組共有的操作進(jìn)行計(jì)時(shí),例如成員資格測(cè)試和屬性訪問。在當(dāng)前系統(tǒng)上運(yùn)行腳本會(huì)顯示類似于以下內(nèi)容的輸出:

      $ namedtuple_dict_time.py namedtuple: 527.26 ns (1.36x faster) dict: 717.71 ns

      此輸出顯示對(duì)命名元組的操作比對(duì)字典的類似操作稍快。

      namedtuple?VS 數(shù)據(jù)類

      Python 3.7帶來了一個(gè)很酷的新特性:數(shù)據(jù)類。根據(jù)PEP 557,數(shù)據(jù)類類似于命名元組,但它們是可變的:

      數(shù)據(jù)類可以被認(rèn)為是“具有默認(rèn)值的可變命名元組”。(來源)

      然而,更準(zhǔn)確地說,數(shù)據(jù)類就像帶有類型提示的可變命名元組。“默認(rèn)”部分根本沒有區(qū)別,因?yàn)槊M也可以為其字段提供默認(rèn)值。因此,乍一看,主要區(qū)別在于可變性和類型提示。

      要?jiǎng)?chuàng)建數(shù)據(jù)類,您需要dataclass()從dataclasses.?然后您可以使用常規(guī)類定義語法定義數(shù)據(jù)類:

      >>>

      >>> from dataclasses import dataclass >>> @dataclass ... class Person: ... name: str ... age: int ... height: float ... weight: float ... country: str = "Canada" ... >>> jane = Person("Jane", 25, 1.75, 67) >>> jane Person(name='Jane', age=25, height=1.75, weight=67, country='Canada') >>> jane.name 'Jane' >>> jane.name = "Jane Doe" >>> jane.name 'Jane Doe'

      在可讀性方面,數(shù)據(jù)類和命名元組之間沒有顯著差異。它們提供類似的字符串表示,您可以使用點(diǎn)表示法訪問它們的屬性。

      在可變性方面,數(shù)據(jù)類根據(jù)定義是可變的,因此您可以在需要時(shí)更改其屬性的值。然而,他們袖手旁觀。您可以將dataclass()裝飾器的frozen參數(shù)設(shè)置為True不可變:

      >>>

      >>> from dataclasses import dataclass >>> @dataclass(frozen=True) ... class Person: ... name: str ... age: int ... height: float ... weight: float ... country: str = "Canada" ... >>> jane = Person("Jane", 25, 1.75, 67) >>> jane.name = "Jane Doe" Traceback (most recent call last): File "", line 1, in File "", line 4, in __setattr__ dataclasses.FrozenInstanceError: cannot assign to field 'name'

      如果您在對(duì) 的調(diào)用中設(shè)置frozen為,那么您將使數(shù)據(jù)類不可變。在這種情況下,當(dāng)您嘗試更新 Jane 的姓名時(shí),您會(huì)得到一個(gè).Truedataclass()FrozenInstanceError

      命名元組和數(shù)據(jù)類之間的另一個(gè)細(xì)微差別是后者默認(rèn)不可迭代。堅(jiān)持 Jane 示例并嘗試迭代她的數(shù)據(jù):

      >>>

      >>> for field in jane: ... print(field) ... Traceback (most recent call last): File "", line 1, in TypeError: 'Person' object is not iterable

      如果您嘗試迭代一個(gè)基本數(shù)據(jù)類,那么您會(huì)得到一個(gè)TypeError.?這在普通課程中很常見。幸運(yùn)的是,有一些方法可以解決它。例如,您可以添加一個(gè).__iter__()特殊的方法來Person像這樣:

      >>>

      >>> from dataclasses import astuple, dataclass >>> @dataclass ... class Person: ... name: str ... age: int ... height: float ... weight: float ... country: str = "Canada" ... def __iter__(self): ... return iter(astuple(self)) ... >>> for field in Person("Jane", 25, 1.75, 67): ... print(field) ... Jane 25 1.75 67 Canada

      在這里,您首先astuple()從dataclasses.?此函數(shù)將數(shù)據(jù)類轉(zhuǎn)換為元組。然后,你將得到的元組iter(),所以你可以建立并返回一個(gè)迭代器從.__iter__()。通過此添加,您可以開始迭代 Jane 的數(shù)據(jù)。

      關(guān)于內(nèi)存消耗,命名元組比數(shù)據(jù)類更輕量級(jí)。您可以通過創(chuàng)建并運(yùn)行一個(gè)類似于您在上一節(jié)中看到的小腳本來確認(rèn)這一點(diǎn)。要查看完整的腳本,請(qǐng)展開下面的框。

      比較內(nèi)存使用情況的腳本:namedtuplevs 數(shù)據(jù)類顯示隱藏

      以下是運(yùn)行腳本的結(jié)果:

      $ python namedtuple_dataclass_memory.py namedtuple: 160 bytes (61.54% smaller) data class: 416 bytes

      與namedtuple類不同,數(shù)據(jù)類保留每個(gè)實(shí)例.__dict__來存儲(chǔ)可寫的實(shí)例屬性。這會(huì)導(dǎo)致更大的內(nèi)存占用。

      接下來,您可以展開以下部分以查看代碼示例,該示例namedtuple根據(jù)屬性訪問的性能比較類和數(shù)據(jù)類。

      比較性能的腳本:namedtuplevs 數(shù)據(jù)類顯示隱藏

      在性能方面,以下是結(jié)果:

      $ python namedtuple_dataclass_time.py namedtuple: 274.32 ns (1.08x faster) data class: 295.37 ns

      性能差異很小,因此您可以說兩種數(shù)據(jù)結(jié)構(gòu)在屬性訪問操作方面的性能相當(dāng)。

      namedtuple?對(duì)比?typing.NamedTuple

      Python 3.5 引入了一個(gè)臨時(shí)模塊,稱為typing支持函數(shù)類型注釋或類型提示。此模塊提供NamedTuple,它是namedtuple.?使用NamedTuple,您可以創(chuàng)建namedtuple帶有類型提示的類。按照Person示例,您可以創(chuàng)建一個(gè)等效的類型命名元組,如下所示:

      >>>

      >>> from typing import NamedTuple >>> class Person(NamedTuple): ... name: str ... age: int ... height: float ... weight: float ... country: str = "Canada" ... >>> issubclass(Person, tuple) True >>> jane = Person("Jane", 25, 1.75, 67) >>> jane.name 'Jane' >>> jane.name = "Jane Doe" Traceback (most recent call last): File "", line 1, in AttributeError: can't set attribute

      使用NamedTuple,您可以通過點(diǎn)表示法創(chuàng)建支持類型提示和屬性訪問的元組子類。由于生成的類是元組子類,因此它也是不可變的。

      在上面的例子中需要注意的一個(gè)微妙細(xì)節(jié)是NamedTuple子類看起來比命名元組更類似于數(shù)據(jù)類。

      在內(nèi)存消耗方面,namedtuple和NamedTuple實(shí)例使用相同數(shù)量的內(nèi)存。您可以展開下面的框以查看比較兩者內(nèi)存使用情況的腳本。

      比較內(nèi)存使用情況的腳本:namedtuplevstyping.NamedTuple顯示隱藏

      這一次,比較內(nèi)存使用情況的腳本產(chǎn)生以下輸出:

      $ python typed_namedtuple_memory.py namedtuple: 160 bytes typing.NamedTuple: 160 bytes

      在這種情況下,兩個(gè)實(shí)例消耗相同數(shù)量的內(nèi)存,因此這次沒有贏家。

      由于namedtuple類和NamedTuple子類都是 的子類tuple,因此它們有很多共同點(diǎn)。在這種情況下,您可以對(duì)字段和值進(jìn)行時(shí)間成員資格測(cè)試。您還可以使用點(diǎn)表示法對(duì)屬性訪問進(jìn)行計(jì)時(shí)。展開下面的框,查看比較兩者的性能腳本namedtuple和NamedTuple。

      比較性能的腳本:namedtuplevstyping.NamedTuple顯示隱藏

      結(jié)果如下:

      $ python typed_namedtuple_time.py namedtuple: 503.34 ns typing.NamedTuple: 509.91 ns

      在這種情況下,您可以說兩種數(shù)據(jù)結(jié)構(gòu)在性能方面的行為幾乎相同。除此之外,NamedTuple用于創(chuàng)建命名元組可以使您的代碼更加明確,因?yàn)槟梢韵蜃侄翁砑宇愋托畔ⅰD€可以為鍵入的命名元組提供默認(rèn)值、添加新功能和編寫文檔字符串。

      在本節(jié)中,您已經(jīng)了解了很多namedtuple其他類似的數(shù)據(jù)結(jié)構(gòu)和類。下面的表格總結(jié)了namedtuple與本節(jié)介紹的數(shù)據(jù)結(jié)構(gòu)的比較:

      通過此摘要,您將能夠選擇最適合您當(dāng)前需求的數(shù)據(jù)結(jié)構(gòu)。此外,您應(yīng)該考慮數(shù)據(jù)類并NamedTuple允許您添加類型提示,這是目前 Python 代碼中非常理想的功能。

      子namedtuple類化

      由于namedtuple類是常規(guī) Python 類,如果您需要提供附加功能、文檔字符串、用戶友好的字符串表示等,您可以將它們子類化。

      例如,在對(duì)象中存儲(chǔ)一個(gè)人的年齡不被認(rèn)為是最佳實(shí)踐。因此,您可能希望存儲(chǔ)出生日期并在需要時(shí)計(jì)算年齡:

      >>>

      >>> from collections import namedtuple >>> from datetime import date >>> BasePerson = namedtuple( ... "BasePerson", ... "name birthdate country", ... defaults=["Canada"] ... ) >>> class Person(BasePerson): ... """A namedtuple subclass to hold a person's data.""" ... __slots__ = () ... def __repr__(self): ... return f"Name: {self.name}, age: {self.age} years old." ... @property ... def age(self): ... return (date.today() - self.birthdate).days // 365 ... >>> Person.__doc__ "A namedtuple subclass to hold a person's data." >>> jane = Person("Jane", date(1996, 3, 5)) >>> jane.age 25 >>> jane Name: Jane, age: 25 years old.

      Person繼承自BasePerson,這是一個(gè)namedtuple類。在子類定義中,您首先添加一個(gè)文檔字符串來描述該類的作用。然后您設(shè)置__slots__為一個(gè)空元組,這會(huì)阻止自動(dòng)創(chuàng)建 per-instance?.__dict__。這使您的BasePerson子類內(nèi)存保持高效。

      您還添加了一個(gè)自定義.__repr__()來為類提供一個(gè)很好的字符串表示。最后,您添加一個(gè)屬性來使用 計(jì)算此人的年齡datetime。

      測(cè)量創(chuàng)建時(shí)間:tuplevsnamedtuple

      到目前為止,您已經(jīng)namedtuple根據(jù)幾個(gè)特性將類與其他數(shù)據(jù)結(jié)構(gòu)進(jìn)行了比較。在本節(jié)中,您將大致了解常規(guī)元組和命名元組在創(chuàng)建時(shí)間方面的比較。

      假設(shè)您有一個(gè)動(dòng)態(tài)創(chuàng)建大量元組的應(yīng)用程序。您決定使用命名元組使您的代碼更加 Pythonic 和可維護(hù)。一旦您更新了所有代碼庫(kù)以使用命名元組,您就可以運(yùn)行應(yīng)用程序并注意到一些性能問題。經(jīng)過一些測(cè)試,您得出結(jié)論,問題可能與動(dòng)態(tài)創(chuàng)建命名元組有關(guān)。

      這是一個(gè)測(cè)量動(dòng)態(tài)創(chuàng)建多個(gè)元組和命名元組所需的平均時(shí)間的腳本:

      # tuple_namedtuple_time.py from collections import namedtuple from time import perf_counter def average_time(test_func): time_measurements = [] for _ in range(1_000): start = perf_counter() test_func() end = perf_counter() time_measurements.append(end - start) return sum(time_measurements) / len(time_measurements) * int(1e9) def time_tuple(): tuple([1] * 1000) fields = [f"a{n}" for n in range(1000)] TestNamedTuple = namedtuple("TestNamedTuple", fields) def time_namedtuple(): TestNamedTuple(*([1] * 1000)) namedtuple_time = average_time(time_namedtuple) tuple_time = average_time(time_tuple) gain = namedtuple_time / tuple_time print(f"tuple: {tuple_time:.2f} ns ({gain:.2f}x faster)") print(f"namedtuple: {namedtuple_time:.2f} ns")

      在此腳本中,您計(jì)算 創(chuàng)建多個(gè)元組及其等效命名元組所需的平均時(shí)間。如果您從命令行運(yùn)行該腳本,您將獲得類似于以下內(nèi)容的輸出:

      $ python tuple_namedtuple_time.py tuple: 7075.82 ns (3.36x faster) namedtuple: 23773.67 ns

      當(dāng)您查看此輸出時(shí),您會(huì)發(fā)現(xiàn)tuple動(dòng)態(tài)創(chuàng)建對(duì)象比創(chuàng)建類似命名的元組快得多。在某些情況下,例如使用大型數(shù)據(jù)庫(kù),創(chuàng)建命名元組所需的額外時(shí)間會(huì)嚴(yán)重影響應(yīng)用程序的性能,因此如果您的代碼動(dòng)態(tài)創(chuàng)建大量元組,請(qǐng)注意這一點(diǎn)。

      結(jié)論

      編寫Pythonic代碼是 Python 開發(fā)領(lǐng)域的一項(xiàng)急需技能。Pythonic 代碼可讀、明確、干凈、可維護(hù),并利用 Python 習(xí)語和最佳實(shí)踐。在本教程中,您學(xué)習(xí)了創(chuàng)建namedtuple類和實(shí)例以及它們?nèi)绾螏椭岣?Python 代碼的質(zhì)量。

      在本教程中,您學(xué)習(xí)了:

      如何創(chuàng)建和使用namedtuple類和實(shí)例

      如何利用很酷的namedtuple功能

      何時(shí)使用namedtuple實(shí)例編寫Pythonic 代碼

      何時(shí)使用 namedtuple而不是類似的數(shù)據(jù)結(jié)構(gòu)

      如何子類化 namedtuple以添加新功能

      有了這些知識(shí),您可以深入提高現(xiàn)有和未來代碼的質(zhì)量。如果您經(jīng)常使用元組,請(qǐng)考慮在有意義的時(shí)候?qū)⑺鼈冝D(zhuǎn)換為命名元組。這樣做將使您的代碼更具可讀性和 Pythonic。

      【生長(zhǎng)吧!Python】有獎(jiǎng)?wù)魑幕馃徇M(jìn)行中:https://bbs.huaweicloud.com/blogs/278897

      Python 數(shù)據(jù)結(jié)構(gòu)

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請(qǐng)聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時(shí)內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:顯示或隱藏表格中的虛框(excel隱藏單元格顯示框)
      下一篇:excel部分網(wǎng)格線不見了(excel里的網(wǎng)格線不見了)
      相關(guān)文章
      色拍自拍亚洲综合图区| 久久精品亚洲视频| 中文字幕 亚洲 有码 在线| 亚洲精品自产拍在线观看动漫| 亚洲精品美女久久久久99| 不卡一卡二卡三亚洲| 在线精品亚洲一区二区三区| 亚洲最大av无码网址| 不卡精品国产_亚洲人成在线| 自拍偷自拍亚洲精品第1页| 国产亚洲精品AA片在线观看不加载| 亚洲av麻豆aⅴ无码电影| 一本色道久久88综合亚洲精品高清| 春暖花开亚洲性无区一区二区 | 亚洲风情亚Aⅴ在线发布| 亚洲人成电影网站免费| 亚洲成AV人片高潮喷水| 久久精品国产亚洲AV未满十八| 极品色天使在线婷婷天堂亚洲 | 小说专区亚洲春色校园| 内射无码专区久久亚洲| 亚洲成A人片在线观看无码3D| 亚洲日本在线观看视频| 亚洲色欲久久久综合网| 国产成A人亚洲精V品无码| 亚洲成人动漫在线| 亚洲日本在线播放| 国产成人精品亚洲日本在线| 亚洲七久久之综合七久久| 亚洲av午夜电影在线观看| 日韩精品成人亚洲专区| 亚洲精品国自产拍在线观看| 区久久AAA片69亚洲| 亚洲AV永久纯肉无码精品动漫| 亚洲性天天干天天摸| 亚洲剧情在线观看| 亚洲欧美国产国产综合一区| 国产99久久亚洲综合精品| 亚洲熟女少妇一区二区| 亚洲视频一区调教| 亚洲一级毛片免观看|