Search code examples
pythonconstructor

Automatically setting class member variables in Python


Say, I have the following class in Python

class Foo(object):
    a = None
    b = None
    c = None
    def __init__(self, a = None, b = None, c = None):
        self.a = a
        self.b = b
        self.c = c

Is there any way to simplify this process? Whenever I add a new member to class Foo, I'm forced to modify the constructor.


Solution

  • Please note that

    class Foo(object):
        a = None
    

    sets a key-value pair in Foo's dict:

    Foo.__dict__['a']=None
    

    while

    def __init__(self, a = None, b = None, c = None):
        self.a = a
    

    sets a key-value pair in the Foo instance object's dict:

    foo=Foo()
    foo.__dict__['a']=a
    

    So setting the class members at the top of your definition is not directly related to the setting of the instance attributes in the lower half of your definition (inside the __init__.

    Also, it is good to be aware that __init__ is Python's initializer. __new__ is the class constructor.


    If you are looking for a way to automatically add some instance attributes based on __init__'s arguments, you could use this:

    import inspect
    import functools
    
    def autoargs(*include,**kwargs):   
        def _autoargs(func):
            attrs,varargs,varkw,defaults=inspect.getargspec(func)
            def sieve(attr):
                if kwargs and attr in kwargs['exclude']: return False
                if not include or attr in include: return True
                else: return False            
            @functools.wraps(func)
            def wrapper(self,*args,**kwargs):
                # handle default values
                for attr,val in zip(reversed(attrs),reversed(defaults)):
                    if sieve(attr): setattr(self, attr, val)
                # handle positional arguments
                positional_attrs=attrs[1:]            
                for attr,val in zip(positional_attrs,args):
                    if sieve(attr): setattr(self, attr, val)
                # handle varargs
                if varargs:
                    remaining_args=args[len(positional_attrs):]
                    if sieve(varargs): setattr(self, varargs, remaining_args)                
                # handle varkw
                if kwargs:
                    for attr,val in kwargs.iteritems():
                        if sieve(attr): setattr(self,attr,val)            
                return func(self,*args,**kwargs)
            return wrapper
        return _autoargs
    

    So when you say

    class Foo(object):
        @autoargs()
        def __init__(self,x,path,debug=False,*args,**kw):
            pass
    foo=Foo('bar','/tmp',True, 100, 101,verbose=True)
    

    you automatically get these instance attributes:

    print(foo.x)
    # bar
    print(foo.path)
    # /tmp
    print(foo.debug)
    # True
    print(foo.args)
    # (100, 101)
    print(foo.verbose)
    # True
    

    PS. Although I wrote this (for fun), I don't recommend using autoargs for serious work. Being explicit is simple, clear and infallible. I can't say the same for autoargs.

    PPS. Is it just me, or are a lot of buttons broken on Stackoverflow? The editor window has lost all its icons... :( Clearing the browser cache fixed the problem.