views:

173

answers:

6

Many times I have member functions that copy parameters into object's fields. For Example:

class NouveauRiches(object):
  def __init__(self, car, mansion, jet, bling):
    self.car = car
    self.mansion = mansion
    self.jet = jet
    self.bling = bling

Is there a python language construct that would make the above code less tedious? One could use *args:

def __init__(self, *args):
  self.car, self.mansion, self.jet, self.bling = args

+: less tedious

-: function signature not revealing enough. need to dive into function code to know how to use function

-: does not raise a TypeError on call with wrong # of parameters (but does raise a ValueError)

Any other ideas? (Whatever your suggestion, make sure the code calling the function does stays simple)

A: 

Try something like

d = dict(locals())
del d['self']
self.__dict__.update(d)

Of course, it returns all local variables, not just function arguments.

tc.
A: 

You could try something like this:

class C(object):
    def __init__(self, **kwargs):
        for k in kwargs:
            d = {k: kwargs[k]}
            self.__dict__.update(d)

Or using setattr you can do:

class D(object):
    def __init__(self, **kwargs):
        for k in kwargs:
            setattr(self, k, kwargs[k])

Both can then be called like:

myclass = C(test=1, test2=2)

So you have to use **kwargs, rather than *args.

Peter Farmer
then, the code calling the function is slightly more complicated
bandana
I wonder why you dont use setattr(self, d, k)
evilpie
@evilpie, I'm fairly new to python, so have updated with answer with your suggestion - thanks!
Peter Farmer
+1  A: 

I would go for this, also you could override already defined properties.

class D:
  def __init__(self, **kwargs):
    self.__dict__.update(kwargs)

But i personally would just go the long way. Think of those:

- Explicit is better than implicit.
- Flat is better than nested.
(The Zen of Python)
evilpie
I don't like this because it won't work with properties or other descriptors -- `self.__dict__.update()` skips all these aspects of objects which you only get with `setattr()`.
Ian Bicking
+1  A: 

I am not sure this is such a good idea, but it can be done:

import inspect
class NouveauRiches(object):
    def __init__(self, car, mansion, jet, bling):
        arguments = inspect.getargvalues(frame)[0]
        values = inspect.getargvalues(frame)[3];
        for name in arguments:
            self.__dict__[name] = values[name]

It does not read great either, though I suppose you could put this in a utility method that is reused.

Tendayi Mawushe
Beautiful is better than ugly.
evilpie
@evilpie Agreed, that is why I prefixed my suggestion with with not recommending this approach.
Tendayi Mawushe
that wasn't against you, i only like citing the zen.
evilpie
+1  A: 

You could do this with a helper method, something like this:

import inspect

def setargs(func):
    f = inspect.currentframe(1)
    argspec = inspect.getargspec(func)
    for arg in argspec.args:
        setattr(f.f_locals["self"], arg, f.f_locals[arg])

Usage:

class Foo(object):

    def __init__(self, bar, baz=4711):
        setargs(self.__init__)

        print self.bar # Now defined
        print self.baz # Now defined

This is not pretty, and it should probably only be used when prototyping. Please use explicit assignment if you plan to have others read it.

It could probably be improved not to need to take the function as an argument, but that would require even more ugly hacks and trickery :)

truppo
Seems unnecessarily convoluted, and also sets self.self. The only advantage I see over `self.__dict__.update(locals())` is that it might be less likely to break if Python ever adds `__foo__` to the output of. On the other hand, it "relies on Python stack frame support in the interpreter, which isn’t guaranteed to exist in all implementations of Python" and makes a reference cycle between stack frames, which is warned against in the docs.
tc.
A: 

I sometimes do this for classes that act "bunch-like", that is, they have a bunch of customizable attributes:

class SuperClass(object):
    def __init__(self, **kw):
        for name, value in kw.iteritems():
            if not hasattr(self, name):
                raise TypeError('Unexpected argument: %s' % name)
            setattr(self, name, value)

class SubClass(SuperClass):
    instance_var = None # default value

class SubClass2(SubClass):
    other_instance_var = True

    @property
    def something_dynamic(self):
        return self._internal_var

    @something_dynamic.setter # new Python 2.6 feature of properties
    def something_dynamic(self, value):
        assert value is None or isinstance(value, str)
        self._internal_var = value

Then you can call SubClass2(instance_var=[], other_instance_var=False) and it'll work without defining __init__ in either of them. You can use any property as well. Though this allows you to overwrite methods, which you probably wouldn't intend (as they return True for hasattr() just like an instance variable).

If you add any property or other other descriptor it will work fine. You can use that to do type checking; unlike type checking in __init__ it'll be applied any time that value is updated. Note you can't use any positional arguments for these unless you override __init__, so sometimes what would be a natural positional argument won't work. formencode.declarative covers this and other issues, probably with a thoroughness I would not suggest you attempt (in retrospect I don't think it's worth it).

Note that any recipe that uses self.__dict__ won't respect properties and descriptors, and if you use those together you'll just get weird and unexpected results. I only recommend using setattr() to set attributes, never self.__dict__.

Also this recipe doesn't give a very helpful signature, while some of the ones that do frame and function introspection do. With some work it is possible to dynamically generate a __doc__ that clarifies the arguments... but again I'm not sure the payoff is worth the addition of more moving parts.

Ian Bicking