Welcome, guest | Sign In | My Account | Store | Cart

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

Python, 20 lines
 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()

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

4 comments

Roberto De Almeida 18 years, 7 months ago  # | flag

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)

.

Frank Patz 18 years, 7 months ago  # | flag

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
amit upadhyay 18 years, 7 months ago  # | flag

Much simpler way.

def __init__(self, **entries):
    self.__dict__.update(entries)
    # rest of initialization..
Ravi Teja Bhupatiraju (author) 18 years, 7 months ago  # | flag

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.

Created by Ravi Teja Bhupatiraju on Sun, 7 Aug 2005 (PSF)
Python recipes (4591)
Ravi Teja Bhupatiraju's recipes (1)

Required Modules

  • (none specified)

Other Information and Tasks