A T
A T

Reputation: 13846

`setattr` fails with `AttributeError` in CPython?

For some reason this fails in Python 3.8:

setattr(iter(()), '_hackkk', 'bad idea')

Error:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-3-c046f8521130> in <module>
----> 1 setattr(iter(()), '_hackkk', 'bad idea')

AttributeError: 'tuple_iterator' object has no attribute '_hackkk'

How do I attach random data where I shouldn't, i.e., on an iterator or a generator?

Upvotes: 3

Views: 174

Answers (1)

ead
ead

Reputation: 34367

You can attach data only to objects, which have __dict__-member. Not all objects have it - for example builtin classes like int, float, list and so on do not. This is an optimization, because otherwise instances of those classes would need too much memory - a dictionary has a quite large memory footprint.

Also for normal classes one could use __slots__, thus removing __dict__-member and prohibiting dynamic addition of attributes to an object of this class. E.g.

class A:
    pass
setattr(A(),'b', 2)

works, but

class B:
    __slots__ = 'b'
setattr(B(),'c', 2)

doesn't work, as class B has no slot with name c and no __dict__.

Thus, the answer to your question is: for some classes (as the tuple_iterator) you cannot.

If you really need to, you can wrap tuple_iterator in a class with __dict__ and append the new attribute to the wrapper-object:

class IterWrapper:
    def __init__(self, it):
        self.it=it
    def __next__(self):
        return next(self.it)   
    def __iter__(self): # for testing
        return self

and now:

iw=IterWrapper(iter((1,2,3)))
setattr(iw, "a", 2)
print(iw.a)          # prints 2
print(list(iw))      # prints [1,2,3]

has the desired behavior.

Upvotes: 1

Related Questions