A
Arnaud Delobelle
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