如何在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$