6

Can I dynamically add attributes to instances of a new-style class (one that derives from object)?

Details:

I'm working with an instance of sqlite3.Connection. Simply extending the class isn't an option because I don't get the instance by calling a constructor; I get it by calling sqlite3.connect().

Building a wrapper doesn't save me much of the bulk for the code I'm writing.

Python 2.7.1

Edit

Right answers all. But I still am not reaching my goal; instances of sqlite3.Connection bar my attempts to set attributes in the following ways (as do instances of object itself). I always get an AttributeError:

> conn = sqlite3.connect([filepath])
> conn.a = 'foo'
Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    conn.a = 'foo'
AttributeError: 'object' object has no attribute 'a'
> conn.__setattr__('a','foo')
Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    conn.__setattr__('a','foo')
AttributeError: 'object' object has no attribute 'a'

Help?

JellicleCat
  • 28,480
  • 24
  • 109
  • 162

3 Answers3

4

Yes, unless the class is using __slots__ or preventing attribute writing by overriding __setattr__, or an internal Python class, or a Python class implemented natively (usually in C).

You can always try setting an attribute. Except for seriously weird __setattr__ implementations, assigning an attribute to an instance of a class of one of the types mentioned above should raise an AttributeError. In these cases, you'll have to use a wrapper, like this:

class AttrWrapper(object):
  def __init__(self, wrapped):
    self._wrapped = wrapped
  def __getattr__(self, n):
    return getattr(self._wrapped, n)
conn = AttrWrapper(sqlite3.connect(filepath))
Eric O. Lebigot
  • 91,433
  • 48
  • 218
  • 260
phihag
  • 278,196
  • 72
  • 453
  • 469
  • I see now that you are correct, but it doesn't work for instances of sqlite3.Connection or `object` itself. (See edit above.) Any help? – JellicleCat May 21 '11 at 16:24
  • @JellicleCat Both fall under "internal Python class". I extended the answer with a wrapper. You mentioned using one in the question; but I'm not sure why it wouldn't solve the problem. If this wrapper does not work, please extend your question (or comment here) with a scenario in which it fails. – phihag May 21 '11 at 16:53
1

Simple experimentation:

In []: class Tst(object): pass
   ..: 
In []: t= Tst()
In []: t.attr= 'is this valid?'
In []: t.attr
Out[]: 'is this valid?'

So, indeed it seems to be possible to do that.

Update:
But from the documentation: SQLite is a C library that ..., so it seems that you really need to wrap it.

eat
  • 7,440
  • 1
  • 19
  • 27
  • I see now that you are correct, but it doesn't work for instances of sqlite3.Connection or `object` itself. (See edit above.) Any help? – JellicleCat May 21 '11 at 16:25
0
    conn.a = 'foo',

or any dynamic assignment is valid, if conn is

    <type 'classobj'>.

Things like:

    c=object() 
    c.e=1

will raise an Attribute error. On the otherhand: Python allows you to do fantastic Metaclass programming:

    >>>from new import classobj
    >>>Foo2 = classobj('Foo2',(Foo,),{'bar':lambda self:'bar'})
    >>>Foo2().bar()
    >>>'bar'
    >>>Foo2().say_foo()
    >>>foo
yet
  • 773
  • 11
  • 19