ActiveState Powered by ActiveState

Recipe 438819: Boring Constructor Pattern


Automate boring constructors that simply create lots of private member variables.

Python
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
def vars_private(func):
    def wrapper(*args):
        for i, arg in enumerate(args[1:]):
            setattr(args[0], '_' + func.func_code.co_varnames[i + 1], arg)
        return func(*args)
    return wrapper


# -- Test --
class Student:
    @vars_private
    def __init__(self, name, age):
        # make young
        self._age = self._age - 2
    
    def info(self):
        return self._name, self._age

s = Student('Ravi Teja', 28)
print s.info()

Discussion

It is all too common to use the constructor to simply assign private variables. This decorator automates that boring code.

So instead of class MyClass: ....def __init__(self, first, last, age, email, address, zip, state, country): ........self._first = first ........self._last = last ........self._age = age ........self._email = email ........self._address = address ........self._zip = zip ........self._state = state ........self._country = country ........print 'New class created'

simply becomes

class MyClass: ....@vars_private ....def __init__(self, first, last, age, email, address, zip, state, country): ........print 'New class created'

Just fiddle with the for loop in the decorator to customerize it further

Gotcha: You can put further code in the constructor but be sure to use the newly created member variables rather than the function arguments as the member variables are not automatically updated by the results of such modifications

Comments

  1. 1. At 6:52 a.m. on 8 aug 2005, Roberto De Almeida said:

    eval() is not necessary. You can remove the eval() call and replace it with just

    setattr(args[0], '_' + func.func_code.co_varnames[i + 1], arg)
    

    .

  2. 2. At 12:14 a.m. on 9 aug 2005, Frank Patz said:

    Defaults and keyword arguments. The following will also handle defaults and keyword arguments. It still does not handle possible errors and will fail badly if you miss the correct signature.

    def vars_private(func):
        def wrapper(*args, **kwargs):
            A = {}
            self = args[0]
            args = args[1:]
            for i, arg in enumerate(func.func_code.co_varnames[1:func.func_code.co_argcount]):
                if i < len(args):
                    A[arg] = args[i]
                elif kwargs.has_key(arg):
                    A[arg] = kwargs[arg]
                else:
                    the_default_arg = i - func.func_code.co_argcount + 1
                    A[arg] = func.func_defaults[the_default_arg]
            for name, value in A.items():
                setattr(self, '_' + name, value)
            return func(self, **A)
        return wrapper
    
  3. 3. At 12:48 a.m. on 16 aug 2005, amit upadhyay said:

    Much simpler way.

    def __init__(self, **entries):
        self.__dict__.update(entries)
        # rest of initialization..
    
  4. 4. At 6:17 a.m. on 28 aug 2005, Ravi Teja Bhupatiraju (the author) said:

    Yes! But ... Simple. Yes! But not quite the same.

    This requires that all instantiations use named arguments which could get tiedious if there are several being done. And argument mismatch at instantiation passes by silently.

Sign in to comment