44

My class has a dict, for example:

class MyClass(object):
    def __init__(self):
        self.data = {'a': 'v1', 'b': 'v2'}

Then I want to use the dict's key with MyClass instance to access the dict, for example:

ob = MyClass()
v = ob.a   # Here I expect ob.a returns 'v1'

I know this should be implemented by __getattr__, but I'm new to Python, I don't exactly know how to implement it.

TieDad
  • 9,143
  • 5
  • 32
  • 58
  • 8
    Ideally, not at all. ;-) –  Apr 26 '13 at 13:28
  • You probably don't really want to do this in practice. If your data belongs in a dict, use a dict; if your data belongs in an object, use an object. Anyway, [`namedtuple`](http://docs.python.org/3.3/library/collections.html#namedtuple-factory-function-for-tuples-with-named-fields), which works kind-of like a lightweight object, might do what you want. – Benjamin Hodgson Apr 26 '13 at 13:30
  • 2
    Remember that `__getattr__` is only used for missing attribute lookup. – Kos Apr 26 '13 at 13:37

8 Answers8

75
class MyClass(object):

    def __init__(self):
        self.data = {'a': 'v1', 'b': 'v2'}

    def __getattr__(self, attr):
        return self.data[attr]

>>> ob = MyClass()
>>> v = ob.a
>>> v
'v1'

Be careful when implementing __setattr__ though, you will need to make a few modifications:

class MyClass(object):

    def __init__(self):
        # prevents infinite recursion from self.data = {'a': 'v1', 'b': 'v2'}
        # as now we have __setattr__, which will call __getattr__ when the line
        # self.data[k] tries to access self.data, won't find it in the instance 
        # dictionary and return self.data[k] will in turn call __getattr__
        # for the same reason and so on.... so we manually set data initially
        super(MyClass, self).__setattr__('data', {'a': 'v1', 'b': 'v2'})

    def __setattr__(self, k, v):
        self.data[k] = v

    def __getattr__(self, k):
        # we don't need a special call to super here because getattr is only 
        # called when an attribute is NOT found in the instance's dictionary
        try:
            return self.data[k]
        except KeyError:
            raise AttributeError

>>> ob = MyClass()
>>> ob.c = 1
>>> ob.c
1

If you don't need to set attributes just use a namedtuple eg.

>>> from collections import namedtuple
>>> MyClass = namedtuple("MyClass", ["a", "b"])
>>> ob = MyClass(a=1, b=2)
>>> ob.a
1

If you want the default arguments you can just write a wrapper class around it:

class MyClass(namedtuple("MyClass", ["a", "b"])):

    def __new__(cls, a="v1", b="v2"):
        return super(MyClass, cls).__new__(cls, a, b)

or maybe it looks nicer as a function:

def MyClass(a="v1", b="v2", cls=namedtuple("MyClass", ["a", "b"])):
    return cls(a, b)

>>> ob = MyClass()
>>> ob.a
'v1'
Neil G
  • 32,138
  • 39
  • 156
  • 257
jamylak
  • 128,818
  • 30
  • 231
  • 230
  • 3
    @EvanLi yes, if by that you mean `ob.data['a']`. You can still set `ob.a = 1` but that will be setting `ob.__dict__['a']` (the instance's dictionary, not yours!) instead. Then it won't be calling `__getattr__` when you access `ob.a`, since `__getattr__` is bypassed when the attribute already exists in the instance – jamylak Apr 26 '13 at 13:35
  • Is there a way to prevent from doing *ob.a = 1*? Maybe to implement \__setattr__ and raise exception? – TieDad Apr 26 '13 at 13:39
  • @EvanLi Yes that's a good idea. However since you don't need to set attributes you can just use a named tuple – jamylak Apr 26 '13 at 13:42
  • Could you give a sample about what's a named tuple? – TieDad Apr 26 '13 at 13:45
  • 1
    The __ getattr __ method should raise AttributeError instead of KeyError if the attribute is not found. Otherwise getattr(obj,key,val) will not work. – Emanuele Paolini Aug 27 '16 at 18:05
  • @jamylak Why do we need to call super(MyClass, self).__setattr__('data', {'a': 'v1', 'b': 'v2'}) why not just set self.data = {'a': 'v1', 'b': 'v2'} ? – himanshu219 Jan 29 '19 at 12:44
  • @himanshu219 There are comments above that line which explain why not to prevent infinite recursion – jamylak Jan 30 '19 at 21:58
  • @Todd `namedtuple` subclassing is great, it's even used in official Python documentation https://docs.python.org/3/library/collections.html#collections.namedtuple Also my approach to avoiding recursion in `__init__` was based off standard practice in the community as well, you say your way is the best but do you have any reason why? It seems a bit worrying to keep track of another flag, adding that into the mix as well as this *bootstrap code* could cause other issues down the line and seems to be no better, I would recommend you submit your full code as an answer – jamylak Mar 06 '20 at 11:38
  • I don't understand why your first example didn't have max recursion error, since you have `self.data` inside `__getattr__`. Can you explain? – KFL Nov 04 '21 at 19:01
  • @KFL In short, it is simply in the instance dictionary already so it will not have any recursion error, it will just get it directly from there. If you want to go more in depth there are articles which talk about how attribute access works in Python eg. I just looked at this: https://medium.com/stepping-through-the-cpython-interpreter/how-does-attribute-access-work-d19371898fee – jamylak Nov 04 '21 at 21:50
  • @KLF @jamylak As I am aware, ``__getattr__`` doesn't have recursion error, because it is a fallback function when ``__getattribute__`` doesn't find proper value. So the steps are somewhat like this: 1) Call __getattribute__(name) On fail: 2) Call __getattr__(name) On fail: 3) Raise exception. On success: 3) Return value. On success: 2) Return value. – oBrstisf8o Nov 30 '22 at 11:19
  • @oBrstisf8o Feel free to share a code example if you wish – jamylak Dec 01 '22 at 00:31
6

Late to the party, but found two really good resources that explain this better (IMHO).

As explained here, you should use self.__dict__ to access fields from within __getattr__, in order to avoid infinite recursion. The example provided is:

def __getattr__(self, attrName):
  if not self.__dict__.has_key(attrName):
     value = self.fetchAttr(attrName)    # computes the value
     self.__dict__[attrName] = value
  return self.__dict__[attrName]

Note: in the second line (above), a more Pythonic way would be (has_key apparently was even removed in Python 3):

if attrName not in self.__dict__:

The other resource explains that the __getattr__ is invoked only when the attribute is not found in the object, and that hasattr always returns True if there is an implementation for __getattr__. It provides the following example, to demonstrate:

class Test(object):
    def __init__(self):
        self.a = 'a'
        self.b = 'b'

    def __getattr__(self, name):
        return 123456

t = Test()
print 'object variables: %r' % t.__dict__.keys()
#=> object variables: ['a', 'b']
print t.a
#=> a
print t.b
#=> b
print t.c
#=> 123456
print getattr(t, 'd')
#=> 123456
print hasattr(t, 'x')
#=> True     
marcelocra
  • 2,094
  • 2
  • 24
  • 37
  • 1
    Thanks for showing a __getattr__ that works and caches the value. Wasn't easy to find. – Gringo Suave Apr 08 '22 at 00:40
  • You don't need to inspect `__dict__` to prevent infinite recursion. `__getattr__` is only called if normal attribute lookup has failed. (This is not true of `__getattribute__` - but the recommendation there is to use `object.__getattribute__`, not `self.__dict__` (because that _will_ invoke __getattribute__ for dict...) – Martin Bonner supports Monica Feb 19 '23 at 08:30
4
class A(object):
  def __init__(self):
     self.data = {'a': 'v1', 'b': 'v2'}
  def __getattr__(self, attr):
     try:
       return self.data[attr]
     except Exception:
       return "not found"


>>>a = A()
>>>print a.a
v1
>>>print a.c
not found
Adem Öztaş
  • 20,457
  • 4
  • 34
  • 42
  • It may be better to raise an exception in this case, instead of returning `None`. Anyway your code can be shortened to `return self.data.get(attr)` – jamylak Apr 26 '13 at 13:36
3

I like to take this therefore.

I took it from somewhere, but I don't remember where.

class A(dict):
    def __init__(self, *a, **k):
        super(A, self).__init__(*a, **k)
        self.__dict__ = self

This makes the __dict__ of the object the same as itself, so that attribute and item access map to the same dict:

a = A()
a['a'] = 2
a.b = 5
print a.a, a['b'] # prints 2 5
glglgl
  • 89,107
  • 13
  • 149
  • 217
  • I thought this was made by Alex Martelli but I'm probably wrong, I found it here http://stackoverflow.com/a/14620633 I believe it goes by the name `AttrDict` – jamylak Apr 26 '13 at 14:03
  • @jamylak It is much older. I just found https://github.com/brickZA/jsobject which was last changed 3 years ago and borrowed it from http://www.jiaaro.com/making-python-objects-that-act-like-javascrip/. They use the name `JsObject`. – glglgl Nov 15 '13 at 08:22
2

I figured out an extension to @glglgl's answer that handles nested dictionaries and dictionaries insides lists that are in the original dictionary:

class d(dict):
    def __init__(self, *a, **k): 
        super(d, self).__init__(*a, **k)
        self.__dict__ = self
        for k in self.__dict__:
            if isinstance(self.__dict__[k], dict):
                self.__dict__[k] = d(self.__dict__[k])
            elif isinstance(self.__dict__[k], list):
                for i in range(len(self.__dict__[k])):
                    if isinstance(self.__dict__[k][i], dict):
                        self.__dict__[k][i] = d(self.__dict__[k][i])
Adam Haile
  • 30,705
  • 58
  • 191
  • 286
1

A simple approach to solving your __getattr__()/__setattr__() infinite recursion woes

Implementing one or the other of these magic methods can usually be easy. But when overriding them both, it becomes trickier. This post's examples apply mostly to this more difficult case.

When implementing both these magic methods, it's not uncommon to get stuck figuring out a strategy to get around recursion in the __init__() constructor of classes. This is because variables need to be initialized for the object, but every attempt to read or write those variables go through __get/set/attr__(), which could have more unset variables in them, incurring more futile recursive calls.

Up front, a key point to remember is that __getattr__() only gets called by the runtime if the attribute can't be found on the object already. The trouble is to get attributes defined without tripping these functions recursively.

Another point is __setattr__() will get called no matter what. That's an important distinction between the two functions, which is why implementing both attribute methods can be tricky.

This is one basic pattern that solves the problem.

class AnObjectProxy:
    _initialized = False # *Class* variable 'constant'.

    def __init__(self):
        self._any_var = "Able to access instance vars like usual."
        self._initialized = True # *instance* variable.

    def __getattr__(self, item):
        if self._initialized:
            pass # Provide the caller attributes in whatever ways interest you.
        else:
            try:
                return self.__dict__[item] # Transparent access to instance vars.
            except KeyError:
                raise AttributeError(item)

    def __setattr__(self, key, value):
        if self._initialized:
            pass # Provide caller ways to set attributes in whatever ways.
        else:
            self.__dict__[key] = value # Transparent access.

While the class is initializing and creating it's instance vars, the code in both attribute functions permits access to the object's attributes via the __dict__ dictionary transparently - your code in __init__() can create and access instance attributes normally. When the attribute methods are called, they only access self.__dict__ which is already defined, thus avoiding recursive calls.

In the case of self._any_var, once it's assigned, __get/set/attr__() won't be called to find it again.

Stripped of extra code, these are the two pieces that are most important.

...     def __getattr__(self, item):
...         try:
...             return self.__dict__[item]
...         except KeyError:
...             raise AttributeError(item)
... 
...     def __setattr__(self, key, value):
...         self.__dict__[key] = value

Solutions can build around these lines accessing the __dict__ dictionary. To implement an object proxy, two modes were implemented: initialization and post-initialization in the code before this - a more detailed example of the same is below.

There are other examples in answers that may have differing levels of effectiveness in dealing with all aspects of recursion. One effective approach is accessing __dict__ directly in __init__() and other places that need early access to instance vars. This works but can be a little verbose. For instance,

self.__dict__['_any_var'] = "Setting..."

would work in __init__().

My posts tend to get a little long-winded.. after this point is just extra. You should already have the idea with the examples above.

A drawback to some other approaches can be seen with debuggers in IDE's. They can be overzealous in their use of introspection and produce warning and error recovery messages as you're stepping through code. You can see this happening even with solutions that work fine standalone. When I say all aspects of recursion, this is what I'm talking about.

The examples in this post only use a single class variable to support 2-modes of operation, which is very maintainable.

But please NOTE: the proxy class required two modes of operation to set up and proxy for an internal object. You don't have to have two modes of operation.

You could simply incorporate the code to access the __dict__ as in these examples in whatever ways suit you.

If your requirements don't include two modes of operation, you may not need to declare any class variables at all. Just take the basic pattern and customize it.

Here's a closer to real-world (but by no means complete) example of a 2-mode proxy that follows the pattern:

>>> class AnObjectProxy:
...     _initialized = False  # This class var is important. It is always False.
...                           # The instances will override this with their own, 
...                           # set to True.
...     def __init__(self, obj):
...         # Because __getattr__ and __setattr__ access __dict__, we can
...         # Initialize instance vars without infinite recursion, and 
...         # refer to them normally.
...         self._obj         = obj
...         self._foo         = 123
...         self._bar         = 567
...         
...         # This instance var overrides the class var.
...         self._initialized = True
... 
...     def __setattr__(self, key, value):
...         if self._initialized:
...             setattr(self._obj, key, value) # Proxying call to wrapped obj.
...         else:
...             # this block facilitates setting vars in __init__().
...             self.__dict__[key] = value
... 
...     def __getattr__(self, item):
...         if self._initialized:
...             attr = getattr(self._obj, item) # Proxying.
...             return attr
...         else:
...             try:
...                 # this block facilitates getting vars in __init__().
...                 return self.__dict__[item]
...             except KeyError:
...                 raise AttributeError(item)
... 
...     def __call__(self, *args, **kwargs):
...         return self._obj(*args, **kwargs)
... 
...     def __dir__(self):
...         return dir(self._obj) + list(self.__dict__.keys())

The 2-mode proxy only needs a bit of "bootstrapping" to access vars in its own scope at initialization before any of its vars are set. After initialization, the proxy has no reason to create more vars for itself, so it will fare fine by deferring all attribute calls to it's wrapped object.

Any attribute the proxy itself owns will still be accessible to itself and other callers since the magic attribute functions only get called if an attribute can't be found immediately on the object.

Hopefully this approach can be of benefit to anyone who appreciates a direct approach to resolving their __get/set/attr__() __init__() frustrations.

Todd
  • 4,669
  • 1
  • 22
  • 30
0

You can initialize your class dictionary through the constructor:

    def __init__(self,**data):

And call it as follows:

f = MyClass(**{'a': 'v1', 'b': 'v2'})

All of the instance attributes being accessed (read) in __setattr__, need to be declared using its parent (super) method, only once:

    super().__setattr__('NewVarName1', InitialValue)

Or

    super().__setattr__('data', dict())

Thereafter, they can be accessed or assigned to in the usual manner:

    self.data = data

And instance attributes not being accessed in __setattr__, can be declared in the usual manner:

    self.x = 1

The overridden __setattr__ method must now call the parent method inside itself, for new variables to be declared:

    super().__setattr__(key,value)

A complete class would look as follows:

class MyClass(object):
    def __init__(self, **data):
        # The variable self.data is used by method __setattr__
        # inside this class, so we will need to declare it 
        # using the parent __setattr__ method:
        super().__setattr__('data', dict())
        self.data = data            
        # These declarations will jump to
        # super().__setattr__('data', dict())
        # inside method __setattr__ of this class:
        self.x = 1
        self.y = 2

    def __getattr__(self, name):
    # This will callback will never be called for instance variables
    # that have beed declared before being accessed.
        if name in self.data:
            # Return a valid dictionary item:
            return self.data[name]
        else:
            # So when an instance variable is being accessed, and
            # it has not been declared before, nor is it contained
            # in dictionary 'data', an attribute exception needs to
            # be raised.
            raise AttributeError

    def __setattr__(self, key, value):
        if key in self.data:
            # Assign valid dictionary items here:
            self.data[key] = value
        else:
            # Assign anything else as an instance attribute:
            super().__setattr__(key,value)

Test:

f = MyClass(**{'a': 'v1', 'b': 'v2'})
print("f.a = ", f.a)
print("f.b = ", f.b)
print("f.data = ", f.data)
f.a = 'c'
f.d = 'e'
print("f.a = ", f.a)
print("f.b = ", f.b)
print("f.data = ", f.data)
print("f.d = ", f.d)
print("f.x = ", f.x)
print("f.y = ", f.y)
# Should raise attributed Error
print("f.g = ", f.g)

Output:

f.a =  v1
f.b =  v2
f.data =  {'a': 'v1', 'b': 'v2'}
f.a =  c
f.b =  v2
f.data =  {'a': 'c', 'b': 'v2'}
f.d =  e
f.x =  1
f.y =  2
Traceback (most recent call last):
  File "MyClass.py", line 49, in <module>
    print("f.g = ", f.g)
  File "MyClass.py", line 25, in __getattr__
    raise AttributeError
AttributeError
Tjaart
  • 496
  • 8
  • 20
0

I think this implement is cooler

class MyClass(object):
    def __init__(self):
        self.data = {'a': 'v1', 'b': 'v2'}
    def __getattr__(self,key):
        return self.data.get(key,None)
mxl
  • 269
  • 1
  • 2
  • 10