Dynamically create class attributes
Asked Answered
D

3

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!

Dingess answered 6/4, 2010 at 8:49 Comment(0)
R
32

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.

Ret answered 6/4, 2010 at 10:16 Comment(2)
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?Amatruda
How do i pass defaults to the file like this a = Settings(defaults) settings.py is a particular fileLorie
S
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
Sesqui answered 6/4, 2010 at 8:54 Comment(5)
how do we pass defaults, or how can we initialize any object of this Settings ClassDiphyodont
same question with @vijayshankerLorie
The code depends on the defaults dictionary being in scope when defining Settings class.Appointment
SettingsMeta and Settings in the setting.py file? How do i pass defaults parameter to it?Lorie
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.Appointment
G
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, ...)
Goolsby answered 6/4, 2010 at 11:42 Comment(1)
-1 The documentation for locals() says the contents of the dictionary returned should not be modified.Kavanagh

© 2022 - 2024 — McMap. All rights reserved.