如何在Python中实现不可变数据结构?

如何在Python中实现不可变数据结构?

阅读更多:Python 教程

问题

您需要在Python中实现不可变数据结构。

简介

不可变数据结构在并行编程中非常方便,可以防止多人同时修改数据。可变数据结构(如数组)可以随时更改,而不可变数据结构则不能。

如何实现

让我逐步展示如何处理不可变和可变数据结构。

例子

# 步骤01-创建可变数组。

# 定义数组
atp_players = ['穆雷', '纳达尔', '德约科维奇']
print(f"***我的数组中原始数据为 - {atp_players}")

***我的数组中原始数据为 –

 ['Murray','Nadal','Djokovic']
# 将球员名称从Murray更改为Federer
atp_players[0] = '费德勒'
print(f"***我的数组中修改后的数据为 - {atp_players}")

***我的数组中修改后的数据为 –

['Federer', 'Nadal', 'Djokovic']

结论

我们已经能够轻松更改数组值,这对于您是该数组的独占用户可能很有用。但是,在实时生产中,多个程序可能在使用此数组进行更改,并可能导致意外的数据。

另一方面,元组的行为有些不同,请参见下面的示例。

# 步骤02-尝试更改元组

try:
atp_players_tuple = ('穆雷', '纳达尔', '德约科维奇')
print(f" ***我的元组中原始数据为 - {atp_players_tuple}")
atp_players_tuple[0] = '费德勒'
except Exception as error:
print(f" ***尝试修改数据,但最终为- {error}")
***我的元组中原始数据为-('Murray','Nadal','Djokovic')
***尝试修改数据,但最终为- 'tuple'对象不支持项目分配

结论:

你所看到的就是,元组不能被修改对吗?但是有一个例外,如果一个元组中包含数组,则可以更改值。

atp_players_array_in_tuple =(['穆雷'],['纳达尔'],['德约科维奇'])
print( f" ***我的元组中带有数组的原始数据为 - {atp_players_array_in_tuple}")

atp_players_array_in_tuple[0] [0] = '费德勒'
print(f" *** 我的元组中带有数组的修改后数据为 - {atp_players_array_in_tuple}")
***我的元组中带有数组的原始数据为-(['Murray'],['Nadal'],['Djokovic'])
***我的元组中带有数组的修改后数据为-(['Federer'],['Nadal'],['Djokovic'])

那么如何保护数据?嗯,将数组转换为元组即可。

try:
atp_players_tuple_in_tuple = (('穆雷'),('纳达尔'),('德约科维奇')) 
print(f" ***我的元组中原始数据为 - {atp_players_tuple_in_tuple}")
atp_players_tuple_in_tuple[0] = '费德勒'
except Exception as error:
print(f" ***尝试在我的元组中修改数据,但最终为- {error}")
***我的元组中原始数据为-('Murray','Nadal','Djokovic')
***尝试在我的元组中修改数据,但最终为-'tuple'对象不支持项目分配

还有更多..Python有一个名为NamedTuple的奇妙内置工具。它是一个可以扩展以创建构造函数的类。让我们以编程方式了解它。

# 用Python的方式创建一个Grandslam头衔的简单类。
class GrandSlamsPythonWay:
def __init__(self, player, titles):
self.player = player
self.titles = titles

stats = GrandSlamsPythonWay("费德勒", 20)
print(f" *** Stat有细节{stats.player} - {stats.titles}")
*** Stat有细节费德勒-20

你认为这个类是不可变的吗?让我们通过将Federer改为Nadal来检查一下。

stats.player = 'Nadal'
print(f" *** Stats has details as {stats.player} - {stats.titles}")
*** Stats has details as Nadal - 20

所以毫不意外它是一个不可变的数据结构,因为我们能够将Federer更新为Nadal。现在让我们使用NamedTuple创建一个类,并看看它的默认行为是什么样的。

from typing import NamedTuple

class GrandSlamsWithNamedTuple(NamedTuple):
player: str
titles: int

stats = GrandSlamsWithNamedTuple("Federer", 20)
print(f" *** Stats has details as {stats.player} - {stats.titles}")

stats.player = 'Djokovic'
print(f" *** Stats has details as {stats.player} - {stats.titles}")
*** Stats has details as Federer - 20
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
in
10 print(f" *** Stats has details as {stats.player} - {stats.titles}")
11
---> 12 stats.player = 'Djokovic'
13 print(f" *** Stats has details as {stats.player} - {stats.titles}")

AttributeError: can't set attribute

看起来Djokovic必须再等一段时间才能获得20个大满贯的冠军头衔。

但是我们可以使用 _replace 方法来复制并在 _replace 过程中更新值。

djokovic_stats = stats._replace(player="Djokovic", titles=17)
print(f" *** djokovic_stats has details as {djokovic_stats.player} - {djokovic_stats.titles}")
*** djokovic_stats has details as Djokovic - 17

示例

最后,我将提供一个包含上述所有内容的示例。

对于此示例,请假装我们正在编写适用于蔬菜店的软件。

from typing import Tuple

# 创建一个表示单次购买的类
class Prices(NamedTuple):
id: int
name: str
price: int # 美元价格

# 创建一个跟踪购买的类
class Purchase(NamedTuple):
purchase_id: int
items: Tuple[Prices]

# 创建蔬菜条目及其相应价格
carrot = Prices(1, "carrot", 2)
tomato = Prices(2, "tomato", 3)
eggplant = Prices(3, "eggplant", 5)

# 现在假设我们的第一个客户Tom先生购买了胡萝卜和番茄
tom_order = Purchase(1, (carrot, tomato))

# 知道我们需要向Tom先生收取的总费用
total_cost = sum(item.price for item in tom_order.items)
print(f"*** Total Cost from Mr.Tom is - {total_cost}$")

输出

*** Total Cost from Mr.Tom is - 5$

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程