Generic constructors and duplication of internal Python logic
John J. Lee
jjl at pobox.com
Thu Apr 22 17:37:14 EDT 2004
Peter Otten <__peter__ at web.de> writes:
> John J. Lee wrote:
>
> > Peter Otten <__peter__ at web.de> writes:
> >
> >> John J. Lee wrote:
> > [...]
> >> You could use a noop method check_attrs() to define the argspec.
> >> check_attrs() is then called from the mixin's __init__() just to check
> >> that the parameters comply.
> >>
> >> import inspect
> >>
> >> def make_attrspec(f):
> >> a = inspect.getargspec(f)
> >> names = a[0]
> >> if names[0] in ["self", "cls"]:
> >> # XXX for now relies on naming convention
> >> del names[0]
> >> defaults = a[3]
> >> for i in range(-1, -len(defaults)-1, -1):
> >> names[i] = names[i], defaults[i]
> >> return names
> >>
> >> class ArgsMixin:
> >> def __init__(self, *args, **kwds):
> >> self.check_attrs(*args, **kwds)
> >>
> >> class Blah(ArgsMixin):
> >> def check_attrs(self, foo, bar, baz, optional1="first",
> >> optional2="second"):
> >> pass
> >> attr_spec = make_attrspec(check_attrs)
> >
> > Clever. But how to get __init__ to assign the arguments to the
> > instance?
> >
> > b = Blah(foo=1, bar=2, optional1=4)
> > assert b.foo, b.bar, b.optional1 = 1, 2, 4
> >
> >
> > That half of the problem is missing in your solution.
>
> I'll give it a try (untested):
>
> class ArgsMixin:
> def __init__(self, *args, **kwds):
> self.check_attrs(*args, **kwds)
> # positionals provided
> for n, v in zip(self.attr_spec, args):
> setattr(self, n, v)
But the actual args may (quite legally) be longer than the args part
of attr_spec, and then:
>>> attr_spec = ["foo", "bar", ("a", "b")]
>>> args = [1, 2]
>>> zip(attr_spec, args)
[('foo', 1), ('bar', 2)]
>>> args = [1, 2, 3]
>>> zip(attr_spec, args)
[('foo', 1), ('bar', 2), (('a', 'b'), 3)] # wrong, can't do setattr!
>>>
This can be fixed, but then we move towards my clumsy-but-correct (I
hope) implementation. I'm disappointed there's no simple solution
that makes better use of Python's own knowledge of argument
specifications.
> # positionals using defaults
> for nv in self.attr_spec[len(args):]:
> if not isinstance(nv, basestring):
> n, v = nv
> if not n in kwds:
> setattr(self, n, v)
> # keyword args
> for n, v in kwds.iteritems():
> setattr(self, n, v)
[...]
John
More information about the Python-list
mailing list