Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically add a class __dict__ property in Python

I am trying to add a __dict__ member to classes generated by namedtuple. (__dict__ is present in python 2.7.3, but was removed in 2.7.5. See http://bugs.python.org/issue15535. It is present and documented in python 3.3.) My code uses vars(nametuple_object), which is based on __dict__. I would like to patch the class when needed.

Here is what I have tried:

# Applies to Python 2.7.5 +
    
C = namedtuple('C', ['x', 'y'])
if not hasattr(C, '__dict__'):
    C.__dict__ = property(C._asdict)

This does not work because C inherits a __dict__, so hasattr is always true and (when forced) the property assignment returns:

Traceback (most recent call last):
  File "namedtuple_dict.py", line 8, in <module>
    C.__dict__ = property(C._asdict)
AttributeError: attribute '__dict__' of 'type' objects is not writable

Perhaps, is there a way to refer to C members that are not inherited?


SOLUTION

Here is my namedtuple wrapper that uses rdb's suggestion of just inheriting from the namedtuple class rather than trying to modify it:

def namedtuple_with_dict(typename, field_names, **kwargs):
    cls = namedtuple(typename, field_names, **kwargs)
    return type(typename, (cls,), {'__dict__': property(cls._asdict)})
like image 984
Demyn Avatar asked Mar 21 '23 21:03

Demyn


1 Answers

namedtuple sets __slots__ to an empty tuple, which is specifically to avoid creating a __dict__ for the sake of memory optimisation. This means that it does not and cannot have __dict__ by its very design. You cannot assign __dict__, or define any new fields for that matter when __slots__ is present.

This does not apply to derived classes, though, so you can do:

CBase = namedtuple('C', ['x', 'y'])

class C(CBase):
    __dict__ = property(CBase._asdict)

print C(1, 2).__dict__
like image 58
rdb Avatar answered Apr 02 '23 07:04

rdb