21

I need to dynamically create class attributes from a DEFAULTS dictionary.

defaults = {
    'default_value1':True,
    'default_value2':True,
    'default_value3':True,
}

class Settings(object):
    default_value1 = some_complex_init_function(defaults[default_value1], ...)
    default_value2 = some_complex_init_function(defaults[default_value2], ...)
    default_value3 = some_complex_init_function(defaults[default_value3], ...)

I could also achive this by having sth. like __init__ for class creation, in order to dynamically create these attributes from dictionary and save a lot of code and stupid work.

How would you do this?

Thank you very much in advance!

SilentGhost
  • 307,395
  • 66
  • 306
  • 293
xyz-123
  • 2,227
  • 4
  • 21
  • 27

3 Answers3

31

You could do it without metaclasses using decorators. This way is a bit more clear IMO:

def apply_defaults(cls):
    defaults = {
        'default_value1':True,
        'default_value2':True,
        'default_value3':True,
    }
    for name, value in defaults.items():
        setattr(cls, name, some_complex_init_function(value, ...))
    return cls

@apply_defaults
class Settings(object):
    pass

Prior to Python 2.6 class decorators were unavailable. So you can write:

class Settings(object):
    pass
Settings = apply_defaults(Settings)

in older versions of python.

In the provided example apply_defaults is reusable… Well, except that the defaults are hard-coded in the decorator's body :) If you have just a single case you can even simplify your code to this:

defaults = {
    'default_value1':True,
    'default_value2':True,
    'default_value3':True,
}

class Settings(object):
    """Your implementation goes here as usual"""

for name, value in defaults.items():
    setattr(Settings, name, some_complex_init_function(value, ...))

This is possible since classes (in the sense of types) are objects themselves in Python.

psmears
  • 26,070
  • 4
  • 40
  • 48
nkrkv
  • 7,030
  • 4
  • 28
  • 36
  • 1
    You also need to return the "decorated" class (`cls` in the first example) at the end of the decorator function, don't you? Took me a while to figure out, but unless I do this the class will equal `None` after definition. Am I missing something, like for example some way of supplying the class by reference instead? – Simon Mar 03 '13 at 10:42
  • How do i pass defaults to the file like this `a = Settings(defaults)` `settings.py` is a particular file – TomSawyer Oct 07 '17 at 07:32
5

I think that's case for metaclass:

class SettingsMeta(type):
    def __new__(cls, name, bases, dct):
        for name, value in defaults.items():
            dct[name] = some_complex_init_function(value, ...)
        return type.__new__(cls, name, bases, dct)

class Settings(object):
    __metaclass__ = SettingsMeta
Łukasz
  • 35,061
  • 4
  • 33
  • 33
  • 2
    how do we pass defaults, or how can we initialize any object of this Settings Class – vijay shanker Nov 11 '15 at 11:22
  • 1
    same question with @vijayshanker – TomSawyer Oct 09 '17 at 08:48
  • The code depends on the `defaults` dictionary being in scope when defining `Settings` class. – Łukasz Oct 09 '17 at 11:33
  • `SettingsMeta` and `Settings` in the setting.py file? How do i pass defaults parameter to it? – TomSawyer Oct 09 '17 at 17:50
  • With the current code, you cannot pass anything, the `defaults` dictionary is taken from the scope, defined before `Settings` class. To be able to pass the defaults, you would need to restructure it. But it feels like a different question to the one asked originally. – Łukasz Oct 09 '17 at 19:27
2

When defining a class, the local namespace will be converted into the class namespace at the conclusion of the class body. As such, you can accomplish this with:

class Settings(object):
    for key, val in defaults.iteritems():
        locals()[key] = some_complex_init_function(val, ...)
Brian
  • 116,865
  • 28
  • 107
  • 112
  • 2
    -1 The [documentation](http://docs.python.org/library/functions.html?highlight=locals#locals) for `locals()` says the contents of the dictionary returned should not be modified. – martineau Nov 16 '10 at 12:26