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