py3k feature proposal: field auto-assignment in constructors

Arnaud Delobelle arnodel at googlemail.com
Mon Jan 28 14:19:33 EST 2008


On Jan 28, 4:08 pm, "André" <andre.robe... at gmail.com> wrote:
[...]
> If I may suggest, I would extend this so that autoassign's signature
> would be as follows:
>
> autoassign(all=True, include_only=None, exclude=None)
>
> Either one of include_only or exclude could be a list of function to
> which the automatic assignment would apply (or not).   I was planning
> to write this up and submit it to the cookbook later this evening, but
> since the suggestion has been made, someone else can jump on it. ;-)
>
> André

I've modified my little decorator (see Test1, Test2, Test3 for
usage).  I'll post it later on the cookbook if there seems to be no
bugs and noone raises valid point against it:)

from functools import wraps
from inspect import getargspec, isfunction
from itertools import izip, ifilter, starmap

def autoassign(*names, **kwargs):
    if kwargs:
        exclude, f = set(kwargs['exclude']), None
        sieve = lambda l:ifilter(lambda nv: nv[0] not in exclude, l)
    elif len(names) == 1 and isfunction(names[0]):
        f = names[0]
        sieve = lambda l:l
    else:
        names, f = set(names), None
        sieve = lambda l: ifilter(lambda nv: nv[0] in names, l)
    def decorator(f):
        fargnames, _, _, fdefaults = getargspec(f)
        # Remove self for fargnames and make sure fdefaults is a tuple
        fargnames, fdefaults = fargnames[1:], fdefaults or ()
        defaults = list(sieve(izip(reversed(fargnames),
reversed(fdefaults))))
        @wraps(f)
        def decorated(self, *args, **kwargs):
            assigned = dict(sieve(izip(fargnames, args)))
            assigned.update(sieve(kwargs.iteritems()))
            # It would be nice to have a builtin to exhaust iterators:
            for _ in starmap(assigned.setdefault, defaults): pass
            self.__dict__.update(assigned)
            return f(self, *args, **kwargs)
        return decorated
    return f and decorator(f) or decorator

class Test(object):
     @autoassign('foo', 'bar')
     def __init__(self, foo, bar=3, baz=6):
         print 'baz =', baz

class Test2(object):
    @autoassign
    def __init__(self, foo, bar): pass

class Test3(object):
    @autoassign(exclude=('foo', 'bar'))
    def __init__(self, foo, bar, baz=5, **kwargs): pass

t = Test(1, 2, 5)
u = Test(foo=8)
v = Test2(10, 11)
w = Test3(100, 101, foobar=102)

print t.foo # 1
print t.bar # 2

print u.foo # 8
print u.bar # 3 (default)

print v.foo, v.bar # 10 11
print w.baz, w.foobar # 5 102

for obj, attr in ('w', 'foo'), ('w', 'bar'), ('t', 'baz'):
    try:
        getattr(globals()[obj], attr)
    except AttributeError:
        print '%s.%s raises AttributeError' % (obj, attr)

==== output ====
baz = 5
baz = 6
1
2
8
3
10 11
5 102
w.foo raises AttributeError
w.bar raises AttributeError
t.baz raises AttributeError

--
Arnaud




More information about the Python-list mailing list