109

Here is my code

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

In the last line I cant set the items[node.ind].v value to node.v as I want, and am getting the error

"AttributeError: can't set attribute"

I don't know what's wrong but it must be something based on syntax as using statements like node.v+=1 is also showing same error. I'm new to Python, so please suggest a way to make the above change possible.

Błażej Michalik
  • 4,474
  • 40
  • 55
Pratyush Dhanuka
  • 1,405
  • 2
  • 11
  • 21

6 Answers6

126

For those searching this error, another thing that can trigger AtributeError: can't set attribute is if you try to set a decorated @property that has no setter method. Not the problem in the OP's question, but I'm putting it here to help any searching for the error message directly. (if you don't like it, go edit the question's title :)

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()
Azmisov
  • 6,493
  • 7
  • 53
  • 70
  • 1
    Small note: For this to work in Python 2 you need to make `Test` a "new-style" class , i.e. explicitly derive from `object`(the first line must read `class Test(object):`. See also https://stackoverflow.com/a/45062077/1753435 – andreee Oct 16 '20 at 08:38
  • I'm having this issue but with a `list` property and cannot figure out how to clear the list, any ideas? @Azmisov – Ahmad Nov 19 '22 at 17:40
  • 1
    @Ahmad I'd need more details, you should create a new question – Azmisov Nov 22 '22 at 20:27
  • Hi @Azmisov it is added here https://stackoverflow.com/questions/74827320/atributeerror-cant-set-attribute-for-python-list-property – Ahmad Dec 16 '22 at 16:37
81
items[node.ind] = items[node.ind]._replace(v=node.v)

(Note: Don't be discouraged to use this solution because of the leading underscore in the function _replace. Specifically for namedtuple some functions have leading underscore which is not for indicating they are meant to be "private")

kroiz
  • 1,722
  • 1
  • 27
  • 43
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
  • 23
    Why?⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ – luckydonald Jun 20 '17 at 22:36
  • 10
    [Because.](https://docs.python.org/3/library/collections.html#collections.somenamedtuple._replace) – Ignacio Vazquez-Abrams Jun 20 '17 at 22:56
  • 36
    @IgnacioVazquez-Abrams more explanation is required in this answer. (linking to the docs is not sufficient.) – abcd Oct 28 '17 at 19:58
  • There is a reason there is an underscore in _replace! This is really bad practice unless you really know what you are doing. See @jonrsharpe for a better solution. – kroiz Jan 16 '18 at 08:17
  • 3
    @kroiz: "There is a reason ..." Yes, there is, and this is it: ["To prevent conflicts with field names, the method and attribute names start with an underscore."](https://docs.python.org/3/library/collections.html#collections.somenamedtuple._replace) – djvg Feb 19 '18 at 13:15
  • @Dennis, What are you saying? Are you supporting my comment or reject it? – kroiz Feb 19 '18 at 20:00
  • 2
    @kroiz Dennis is saying we use underscore for methods and attributes to distinguish them from field names, jonrsharpe gave a detailed answer, but my question is about getting the best way to correct the error, using a inbuilt functionality is always better.. Even jonrsharpe mentioned that part.. Frankly i would have accepted both but stack dont allow that – Pratyush Dhanuka Feb 20 '18 at 08:47
  • According to PEP-8: "_single_leading_underscore: weak "internal use" indicator." So which is in this case? is the underscore meant to indicate internal use? or to just to prevent some name conflict? – kroiz Feb 21 '18 at 06:58
  • @kroiz: The quote in my comment above comes directly from the python 3 docs, so I would guess the latter. – djvg Feb 21 '18 at 07:07
  • Ok, Now I understand. So it is just specific to nametuple that there are some functions with underscore which *are* meant to be used as "public". – kroiz Feb 21 '18 at 07:25
  • 7
    TLDR; This is a valid solution. The leading underscore in the function _replace was not meant to indicate internal usage. – kroiz Feb 21 '18 at 07:28
  • 1
    This discussion is so useless. Either something is or it isn't. This is harmful and a bad developer experience. – ThaJay Jan 11 '19 at 11:40
  • @djvg: People were confused because you linked to a bit in the docs that is *after* the part you quoted. (Unless they've moved it since.) – Timmmm Oct 29 '21 at 14:36
76

namedtuples are immutable, just like standard tuples. You have two choices:

  1. Use a different data structure, e.g. a class (or just a dictionary); or
  2. Instead of updating the structure, replace it.

The former would look like:

class N(object):

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

And the latter:

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

If you want the latter, Ignacio's answer does the same thing more neatly using baked-in functionality.

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
3

This error can be triggered if you try to redefine a member variable that is already defined in the class you inherited.

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

As I was new to PyTorch and PyTorch Lightning, I did not know the LightningModule already had a member variable named self.hparams. As I tried to overwrite it in my code, it caused AttributeError: can't set attribute.

Just simply renaming my variable from self.hparams to something else removed the error.

Not the problem in the OP's question, but I'm putting it here to help any searching for the error message directly

Matt Yoon
  • 396
  • 4
  • 11
1

I came across this when I incorrectly mixed dataclass and NamedTuple. Posting this here to potentially save someone from tearing out their hair.

@dataclasses.dataclass
class Foo(typing.NamedTuple):
    bar: str
Hannes Landeholm
  • 1,525
  • 2
  • 17
  • 32
0

In addition to this answer that Azmisov provided, adding a setter would solve the problem:

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()