[已解决] AttributeError: 不能在python中设置属性

6,015 阅读5分钟

问题的提出

你在代码中创建了一个 [namedtuple](https://blog.finxter.com/the-ultimate-guide-to-python-tuples/#Named_Tuples "https://blog.finxter.com/the-ultimate-guide-to-python-tuples/#Named_Tuples")对象,你想覆盖其中的一个属性值,就像你对普通对象所习惯的那样。

from collections import namedtuple

Car = namedtuple('Car', 'speed color')
porsche = Car(300, 'gold')
porsche.speed = 400

但是,出乎意料的是,Python 提出了一个 **AttributeError: can't set attribute**.😖

Traceback (most recent call last):
  File "C:\Users\xcent\Desktop\code.py", line 5, in <module>
    porsche.speed = 400
AttributeError: can't set attribute

你能做什么?🤔 让我们了解这个错误发生的原因--之后你就会知道最好的解决方法。

为什么会发生这个错误?

任何nametuple 对象也是一个 [tuple](https://blog.finxter.com/the-ultimate-guide-to-python-tuples/ "The Ultimate Guide To Python Tuples")(子类关系),所以它是不可改变 的--你不能在创建后改变它。例如,如果不创建一个新的元组('Alice', 'Bob') ,你就不能把它改成('Ann', 'Bob')

如果你试图改变一个namedtuple 对象的属性值,你就试图修改一个不可变的对象。因此,Python 引发了AttributeError: can't set attribute

修复1:使用 namedtuple._replace() 方法

修复AttributeError:can't set attribute ,最简单的方法是用namedtuple 对象创建一个新的 [namedtuple._replace()](https://docs.python.org/3/library/collections.html#collections.somenamedtuple._replace "https://docs.python.org/3/library/collections.html#collections.somenamedtuple._replace")方法创建一个新的 对象。例如,如果你想把命名元组对象n 的属性x 改为值42 ,调用n._replace(x=42) 。结果是一个新的namedtuple 对象,所有的属性都被复制,除了新传递的属性。最后,用新的namedtuple 覆盖原始变量n

这里是应用于我们前面例子的修复方法。

from collections import namedtuple

Car = namedtuple('Car', 'speed color')
porsche = Car(300, 'gold')
porsche = porsche._replace(speed=400)

你可以在下面的语句中看到 [print()](https://blog.finxter.com/python-print/ "Python print()")语句中,speed 的原始属性值已经从300变成了400。

print(porsche)
# Car(speed=300, color='gold')

注意,这通常是低效的,因为所有其他的属性都必须被复制,只是为了更新一个单一的属性!

我们可以解决这个问题吗?当然可以!😅

修复2。不要使用命名图元而是使用类

Namedtuples 是不可变的对象,所以你不能改变属性值。但是标准的 Python 类是可变的,所以它们可以被任意地改变。具体来说,你_可以_ 改变一个现有属性的值,甚至可以使用 "鸭子类型 "动态地给一个现有对象添加一个新属性。

下面的代码与我们的例子代码完成了同样的事情--但它使用标准的Python类而不是namedtuples ,更有效地完成了这个任务。

class Car:
    def __init__(self, speed, color):
        self.speed = speed
        self.color = color

porsche = Car(300, 'gold')
porsche.speed = 400

让我们确认一下,属性值已经变成了400。

print(porsche.speed)
# 400

你甚至可以像这样给对象添加一个额外的属性,例如price

porsche.price = 100000
print(porsche.price)
# 100000

修复方法#3:使用字典

对于轻量级的应用程序,你也可以使用一个简单的字典,而不是命名的图元或类。字典被设计用来保存_键:值_而不是_属性:值_对。例如,你可以创建一个 dictionary{'key_1': 'value_1', 'key_2': 'value_2'} 来存储两个键而不是属性。现在你可以使用dict['key_1'] = 'new_value' 来更新一个键,而不是更新一个属性。

下面是这个解决方案应用于我们之前的问题--发生了相当大的简化😄

porsche = {'speed': 300, 'color': 'gold'}
porsche['speed'] = 400

让我们检查一下属性是否已经改变。

print(porsche['speed'])
# 400

的确,输出已经改变,而且没有错误信息存在。

修复4:如果你必须使用Namedtuples,使用一个可变的属性

如果你必须使用namedtuples ,并且你需要用一个新的值来更新一个现有的属性,并且由于效率的原因你不能用_replace() 来创建一个新的属性,你可以做以下的事情。

nametuple 对象的属性创建为可改变的对象,如列表。你总是可以改变一个列表的内容,因为它是可变的。因此,从语义上讲,你修改namedtuple 属性的值,实际上并没有违反不变性标准--namedtuple 仍然指向内存中的同一个可变对象(例如,列表)。

下面是这个应用于例子问题的解决方案。

from collections import namedtuple

Car = namedtuple('Car', 'speed color')
porsche = Car([300], 'gold')
porsche.speed[0] = 400

虽然你可能不会觉得这个解决方案特别性感,但它却像一个魅力。

print(porsche.speed)
# [400]

速度的原始值已经改变,而且没有发生错误。

从哪里开始?

理论够多了,让我们去实践一下吧!

要想在编码方面取得成功,你需要走出去,为真正的人解决真正的问题。这样你才能轻松成为六位数的收入者。而这也是你如何在实践中打磨出你真正需要的技能。毕竟,学习没有人需要的理论有什么用?

实践项目就是你在编码中磨练你的锯子的方法!

你想通过专注于实际的代码项目,成为一个代码大师,真正为你挣钱,为人们解决问题吗?

那就成为Python的自由开发者吧!这是接近提高Python技能任务的最好方式--即使你是一个完全的初学者。

参加我的免费网络研讨会"如何建立你的高收入技能Python",看看我是如何在网上发展我的编码业务的,你也可以这样做--从你自己家里的舒适度。

现在就参加免费网络研讨会

The post[Solved] AttributeError: can't set attribute in pythonfirst appeared onFinxter.