21

Python's setdefault allows you to get a value from a dictionary, but if the key doesn't exist, then you assign the based on parameter default. You then fetch whatever is at the key in the dictionary.

Without manipulating an object's __dict__Is there a similar function for objects?

e.g.
I have an object foo which may or may not have attribute bar. How can I do something like:

result = setdefaultattr(foo,'bar','bah')
Ross Rogers
  • 23,523
  • 27
  • 108
  • 164

4 Answers4

28

Note that the currently accepted answer will, if the attribute doesn't exist already, have called hasattr(), setattr() and getattr(). This would be necessary only if the OP had done something like overriding setattr and/or getattr -- in which case the OP is not the innocent enquirer we took him for. Otherwise calling all 3 functions is gross; the setattr() call should be followed by return value so that it doesn't fall through to return getattr(....)

According to the docs, hasattr is implemented by calling getattr() and catching exceptions. The following code may be faster when the attribute exists already:

def setdefaultattr(obj, name, value):
    try:
        return getattr(obj, name)
    except AttributeError:
        setattr(obj, name, value)
    return value
Jean-Francois T.
  • 11,549
  • 7
  • 68
  • 107
John Machin
  • 81,303
  • 11
  • 141
  • 189
9

Python doesn't have one built in, but you can define your own:

def setdefaultattr(obj, name, value):
    if not hasattr(obj, name):
        setattr(obj, name, value)
    return getattr(obj, name)
Ned Batchelder
  • 364,293
  • 75
  • 561
  • 662
  • 1
    What about `def setdefaultattr(o,n,v): return setdefault(o.__dict__, n, v)`? That wouldn't work on classes with slots though. – Georg Schölly Dec 15 '09 at 07:02
4
vars(obj).setdefault(name, value)
John La Rooy
  • 295,403
  • 53
  • 369
  • 502
  • So vars() just returns obj.__dict__? – Ross Rogers Dec 15 '09 at 01:49
  • ( I do prefer the syntax of var() to obj.__dict__ ) – Ross Rogers Dec 15 '09 at 01:50
  • Yup, it does: >>> class Foo(object): ... def __init__(self): self.x = 'foo' ... >>> f = Foo() >>> vars(f) is f.__dict__ True – Mike Hordecki Dec 15 '09 at 01:53
  • 10
    From python docs **Note The returned dictionary should not be modified: the effects on the corresponding symbol table are undefined.** – nosklo Dec 15 '09 at 03:23
  • 3
    @noskio, That is certainly the case for `vars()` with no parameters as this returns `locals()`. Attempting to change `locals()` does not work. However there seems to be a contradiction with the builtin docs saying that `vars(obj)` is equivalent to `obj.__dict__` as `obj.__dict__` can be modified. – John La Rooy Dec 15 '09 at 07:01
  • Seems to me that `vars(obj)` is just a slower way of saying `obj.__dict__` because it requires an extra function call. It looks a little nicer, I suppose, but it's hard to understand why such a built-in even exists... – martineau Dec 23 '12 at 18:10
  • @martineau in general, client code isn't supposed to use dunder methods directly. Having a wrapper like `vars` offers a convenient place to put any other logic if the dev team deems it appropriate in future Python versions (like how `len` builds upon `__len__` methods by verifying the return type). – Karl Knechtel Feb 10 '23 at 22:33
  • This is not possible in modern versions of Python. The attempt to assign a key in either the `vars` result or `__dict__` attribute will raise `TypeError: 'mappingproxy' object does not support item assignment`. I am not certain how long this has been the case - I suspect it was brought in right with 3.0. – Karl Knechtel Feb 10 '23 at 22:35
-2

Don't Do This.

Please use __init__ to provide default values. That's the Pythonic way.

class Foo( object ):
    def __init__( self ):
        self.bar = 'bah'

This is the normal, standard, typical approach. There's no compelling reason to do otherwise.

Noam Hacker
  • 4,671
  • 7
  • 34
  • 55
S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • 2
    You're right. Shame on me :-) I should just go fix the __init__ function. – Ross Rogers Dec 15 '09 at 17:33
  • 1
    There might very well be reasons to do what was asked for, e.g. caching temporary results in properties, which you only need, when the attribute is actually read. Besides that, what you're doing in your example should better be a plain class variable bar. What you're doing ist just bloating every single object without reason. – Michael Jan 07 '13 at 16:32
  • 3
    In most cases this is correct, but there are exceptions. For example, when writing descriptors many folks choose to put relevant data as attributes (with name mangling!) on the instances managed by the descriptor. – DanielSank Nov 02 '14 at 17:19