idiom for constructor?

M

Mac

Is there a nice Python idiom for constructors which would expedite the
following?

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

I would like to keep the __init__ parameter list explicit, as is,
rather than passing in a dictionary, as I want the code to be explicit
about what arguments it expects... in effect enforcing the right number
of arguments.
 
C

Chris Green

Mac said:
Is there a nice Python idiom for constructors which would expedite the
following?

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

You could try:

class Foo:
def __init__(self,a,b,c,d):
args = locals()
for arg in args.keys():
if name!='self':
self.__dict__[arg] = args[arg]

I don't think it saves you a whole lot of typing (and gains you a lot
of ugly complexity). I find as soon as I do this, I then want to
manipulate a,b,c,d .

You might look to see if you can customize your editor to use
templates/interaction and then inserts the right text for you.
 
M

Mac

You might look to see if you can customize your editor to use
templates/interaction and then inserts the right text for you.

Well, I'm not really concerned with "amount of typing" as much as with
the inherent ugliness, tediousness, and lack of elegance of the
original form. Alas, templates/macros would not fix the latter.
 
L

Laszlo Zsolt Nagy

Mac said:
Is there a nice Python idiom for constructors which would expedite the
following?

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

I would like to keep the __init__ parameter list explicit, as is,
rather than passing in a dictionary, as I want the code to be explicit
about what arguments it expects... in effect enforcing the right number
of arguments.
I could list the parameter names programatically:

class A(object):
def __init__(self,a,b,c,d,e,f,):
varnames = self.__init__.im_func.func_code.co_varnames
for varname in varnames[1:7]:
print varname

a = A(1,2,3,4,5,6)

But I could not get their values.
 
L

Laszlo Zsolt Nagy

You could try:

class Foo:
def __init__(self,a,b,c,d):
args = locals()
for arg in args.keys():
if name!='self':
self.__dict__[arg] = args[arg]
Should be:

if arg!='self'

Also it is not perfect. For example:

class Foo:
def __init__(self,a,b,c,d):
temp = 12
self.foo2 = temp + 4
args = locals()
for arg in args.keys():
if arg!='self':
self.__dict__[arg] = args[arg]

a = Foo(1,2,3,4)
print dir(a)

Results in:

['__doc__', '__init__', '__module__', 'a', 'b', 'c', 'd', 'foo2', 'temp']

E.g. not only the parameters but all local variables are used...
 
S

Steven Bethard

Mac said:
Is there a nice Python idiom for constructors which would expedite the
following?

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

py> class Foo(object):
.... def __init__(self, a, b, c, d):
.... params = locals()
.... del params['self']
.... self.__dict__.update(params)
....
py> vars(Foo(1, 2, 3, 4))
{'a': 1, 'c': 3, 'b': 2, 'd': 4}

Just make sure that "params = locals()" is the first line in __init__
method. (Otherwise you might have other local variables slip in there.)

I wouldn't actually advise this code to anyone though. I find that if I
have more than 3 or 4 parameters to a function, I should probably
refactor my code. And with 4 or fewer parameters, I don't think you
gain any clarity by using the "self.__dict__.update" trick.

STeVe
 
T

Terry Hancock

Is there a nice Python idiom for constructors which would expedite the
following?

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

I would like to keep the __init__ parameter list explicit, as is,
rather than passing in a dictionary, as I want the code to be explicit
about what arguments it expects... in effect enforcing the right number
of arguments.

Well, it's hard to automate it and keep the parameter list explicit,
but you could do this, of course:

class Foo:
def __init__(self, *args):
maps = zip(('a','b','c','d'), args[:4])
for map in maps:
setattr(self, map[0], map[1])

Which *will* limit the arguments to the ones specified. As an additional
tweak, you could make this a base class and put the argument list in
the subclass to hide the magic:

class Args:
argspec = ()
def __init__(self, *args):
maps = zip(self.argspec, args[:len(self.argspec)])
for map in maps:
setattr(self, map[0], map[1])

class Foo(Args):
argspec = ('a', 'b', 'c', 'd')

Or even better, use a method, so you can customize:

class Args:
argspec = ()
def _get_args(self, *args):
maps = zip(self.argspec, args[:len(self.argspec)])
for map in maps:
setattr(self, map[0], map[1])

class Foo(Args):
argspec = ('a', 'b', 'c', 'd')
def __init__(self, *args):
self._get_args(*args)

This version silently ignores extra arguments, but you might
want to raise an exception instead:

class Args:
argspec = ()
def _get_args(self, *args):
expected = len(self.argspec)
given = len(args)
if expected != given:
raise TypeError("__init__ takes exactly %d arguments (%d given)" % (expected, given))
maps = zip(self.argspec, args[:expected])
for map in maps:
setattr(self, map[0], map[1])

Using this, I get the following response to too many arguments:
File "<stdin>", line 1, in ?
File "<stdin>", line 4, in __init__
File "<stdin>", line 7, in _get_args
TypeError: __init__ takes exactly 4 arguments (5 given)

HTH

Cheers,
Terry
 
T

Terry Reedy

Mac said:
Is there a nice Python idiom for constructors which would expedite the
following?

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

Do you like this better?

self.a, self.b, self.c, self.d = a,b,c,d

For my own code, I might use 's' instead of 'self' to save typing...

Terry J. Reedy
 
M

Mac

This was the direction I was aiming for initially, and I have used this
form before, but was hoping there was a way I could go one step
further, and somehow get rid of the repetition of "self."... Heh,
ideally:

self.{a,b,c,d} = a,b,c,d

Alas, not Python syntax... :)

I do like Steven's form of a solution, and I think I'll give it a spin
for the really ugly spots (refactoring is too expensive right now;
prototype-code).
 
P

Peter Dembinski

Steven Bethard said:
Mac said:
Is there a nice Python idiom for constructors which would expedite
the following?
class Foo:
def __init__(self, a,b,c,d,...):
self.a = a
self.b = b
self.c = c
self.d = d
...

py> class Foo(object):
... def __init__(self, a, b, c, d):
... params = locals()
... del params['self']
... self.__dict__.update(params)
...
py> vars(Foo(1, 2, 3, 4))
{'a': 1, 'c': 3, 'b': 2, 'd': 4}

Just make sure that "params = locals()" is the first line in
__init__ method. (Otherwise you might have other local variables
slip in there.)

Or write it like this, using Python's dynamic code execution:

#v+

class A:
def __init__(self, a, b, c, d):
initial = {'a' : 1, 'b' : 2, 'c' : 3, 'd' : 4}

for param in initial.keys():
exec "self.%s = initial['%s']" % (param, param)

#v-
 
P

Peter Dembinski

[snap]

Eh, sorry, it should look like this:
#v+

class A:
def __init__(self, a, b, c, d):
initial = {'a' : 1, 'b' : 2, 'c' : 3, 'd' : 4}
initial = {'a' : a, 'b' : b, 'c' : c, 'd' : d}
for param in initial.keys():
exec "self.%s = initial['%s']" % (param, param)

#v-
 
S

Steven Bethard

Peter said:
class A:
def __init__(self, a, b, c, d):
initial = {'a' : a, 'b' : b, 'c' : c, 'd' : d}
for param in initial.keys():
exec "self.%s = initial['%s']" % (param, param)

This is not a good use case for exec. Use setattr:

for param in initial:
setattr(self, param, initial[param])

Or better yet, just update the instance dict:

self.__dict__.update(initial)

But this misses the OP's point. The issues was that the OP didn't want
to write a, b, c and d again. Not only does this proposal make you
write them again, it makes you write them again twice!

STeVe
 
P

Peter Dembinski

Steven Bethard said:
Peter said:
class A:
def __init__(self, a, b, c, d):
initial = {'a' : a, 'b' : b, 'c' : c, 'd' : d}
for param in initial.keys():
exec "self.%s = initial['%s']" % (param, param)

This is not a good use case for exec. Use setattr:

OK, true.
From the other side: what are the usual uses of 'exec'?
 
V

Volker Grabsch

Peter said:
OK, true.
From the other side: what are the usual uses of 'exec'?

An interactive Python interpreter. :)

No, seriously: Introspection is always better than exec.
It is far less error phrone, especially because you don't need
to deal with quoting issues, and because you have an initial
syntax check of your Python code which is bypassed when using exec.

Similarly, try to avoid system() whenever possible.


Greets,
 
S

Steven Bethard

Peter said:
From the other side: what are the usual uses of 'exec'?

I have to say, I have yet to find a use for it.

My intuition is that the good use cases for 'exec' have something to do
with writing programs that allow users to interactively script the
program actions. But I've never written such a program, so I can't
speak much about it.

STeVe
 
T

tracyshaun

How about just doing this:

class Foo(object):
__slots__ = ('a','b','c','d')
def __init__(self, *args):
for (name, arg) in zip(self.__slots__, args):
setattr(self, name, arg)

--T
 
T

tracyshaun

And you probably should add:

...
def __init__(self, *args):
assert len(args) == len(self.__slots__)
...

--T
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,241
Messages
2,571,219
Members
47,849
Latest member
RoseannKoz

Latest Threads

Top