属性错误:在Python中无法设置属性

110

这是我的代码

N = namedtuple("N", ['ind', 'set', 'v'])
def solve():
    items=[]
    stack=[]
    R = set(range(0,8))
    for i in range(0,8):
        items.append(N(i,R,8))      
        stack.append(N(0,R-set(range(0,1)),i))
    while(len(stack)>0): 
        node = stack.pop()
        print node
        print items[node.ind]   
        items[node.ind].v = node.v

在最后一行,我无法将items[node.ind].v的值设置为node.v,我想要这样做,但是出现了错误。
"AttributeError: can't set attribute"

我不知道出了什么问题,但它一定基于句法,因为使用像node.v+=1这样的语句也显示相同的错误。 我很新于Python,请建议一种使上述更改变得可行的方法。

谢谢回答,但是在像 items[i].v <8 这样的比较中,我该怎么做呢?我刚试着使用一个临时变量来存储它的值,然后用这个变量进行比较,如 temp = items[i].v 然后 temp<8 - Pratyush Dhanuka
访问namedtuple的属性时无需采取特殊措施。 - Ignacio Vazquez-Abrams
访问没有问题,但我无法重新分配它,我理解这一点,这也是为什么我无法像我提到的 items[i].v]<8 一样用它进行比较的原因。我需要知道是否有任何更好的替代方法,就像答案中使用 ._replace() 一样。 - Pratyush Dhanuka
在表达式中使用它不需要替换它。 - Ignacio Vazquez-Abrams
是的,它起作用了,不知道为什么之前在那个语句处出错了 :C - Pratyush Dhanuka
7个回答

133

对于那些搜索此错误的人,触发AtributeError: can't set attribute的另一件事是,如果您尝试设置一个没有setter方法的装饰过的@property,也会出现这个错误。这不是OP问题中的问题,但我把它放在这里直接帮助任何搜索错误消息的人。(如果你不喜欢它,请编辑问题的标题:)

class Test:
    def __init__(self):
        self._attr = "original value"
        # This will trigger an error...
        self.attr = "new value"
    @property
    def attr(self):
        return self._attr

Test()

1
小提示:为了在 Python 2 中使其工作,您需要将Test定义为“新式”类,即显式从object派生(第一行必须写成class Test(object):)。另请参见 https://dev59.com/hG865IYBdhLWcg3wCqHI#45062077。 - andreee
我遇到了这个问题,但是出现在一个“列表”属性中,我无法弄清楚如何清空该列表,有任何想法吗?@Azmisov - Ahmad
1
@Ahmad 我需要更多细节,你应该创建一个新问题。 - Azmisov
嗨@Azmisov,已将其添加在此处https://dev59.com/TMXsa4cB1Zd3GeqPnp11 - Ahmad

84
items[node.ind] = items[node.ind]._replace(v=node.v)
(Note: 不要因为函数_replace中的前导下划线而放弃使用此解决方案。对于命名元组,一些函数具有前导下划线,这并不意味着它们是“私有”的。)

23
为什么? - luckydonald
10
因为。 - Ignacio Vazquez-Abrams
37
@IgnacioVazquez-Abrams 这个回答需要更多的解释。仅仅提供文档链接是不够的。 - abcd
3
“@kroiz: "There is a reason ..."(有一个原因...)”是对的,这就是原因:“为了避免与字段名称冲突,方法和属性名称以下划线开头。” (来源:https://docs.python.org/3/library/collections.html#collections.somenamedtuple._replace) - djvg
7
这是一个有效的解决方案。函数_replace中的下划线并不意味着它只能在内部使用。 - kroiz
显示剩余8条评论

76

namedtuple是不可变的,就像标准元组一样。你有两个选择:

  1. 使用不同的数据结构,例如类(或仅仅是字典);或者
  2. 不是更新结构,而是替换它。

前者看起来像:

class N(object):

    def __init__(self, ind, set, v):
        self.ind = ind
        self.set = set
        self.v = v

而后者:

item = items[node.ind]
items[node.ind] = N(item.ind, item.set, node.v)

如果你想要更好的实现,Ignacio的答案使用内置功能执行同样的操作。

是的,在10分钟后我按照你在后面告诉我的做了,现在我明白为什么这个方法有效而之前的方法不行。谢谢。 - Pratyush Dhanuka
5
命名元组是不可变的 - 这句话说得很准确。谢谢。 - Tinkerbell
3
可以使用不同的数据结构,比如一个类(class),可能是一个 @dataclass。 - Nickolay

3

如果您尝试重新定义已在继承的类中定义的成员变量,则可能会触发此错误。

from pytorch_lightning import LightningModule

class Seq2SeqModel(LightningModule):
    def __init__(self, tokenizer, bart, hparams):
        super().__init__()
        self.tokenizer = tokenizer
        self.bart: BartForConditionalGeneration = bart
        self.hparams = hparams  # This triggers the error
        # Changing above line to below removes the error
        # self.hp = hparams

由于我对PyTorchPyTorch Lightning不太熟悉,因此我不知道LightningModule已经有了一个名为self.hparams的成员变量。当我试图在我的代码中覆盖它时,它导致了AttributeError: can't set attribute错误。

将我的变量从self.hparams简单地重命名为其他名称即可消除错误。

这并不是OP问题中的问题,但我在这里放置它以帮助任何直接搜索该错误消息的人。


是的,终于得到了正确的答案,谢谢! - xxfelixxx

1

我在错误地混合使用dataclass和NamedTuple时遇到了这个问题。在这里发布它,可能会帮助某些人避免拔光头发。

@dataclasses.dataclass
class Foo(typing.NamedTuple):
    bar: str

0
除了 Azmisov 提供的 this answer 之外,添加 setter 将解决这个问题:
class Test:
    def __init__(self):
        self._attr = "original value"
        # This will trigger an error...
        self.attr = "new value"

    @property
    def attr(self):
        return self._attr

    @attr.setter
    def attr(self, value):
        self._attr = value


Test()

-2
对于其他通过谷歌搜索到这条信息的人,我遇到了这个错误是因为复制/粘贴错误。 设置器的def方法不存在正确的名称。 例如:
def __init__(self,a):
     self.a = a

...

@property
def a(self):
    return self._a
@a.setter
def b(self, value): # Should have used a(self,value):
    self._a = value

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接