This recipe defines a decorator @autoassign that makes methods assign some or all of their arguments automatically to attributes of self.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 | from functools import wraps
from inspect import getargspec, isfunction
from itertools import izip, ifilter, starmap
def autoassign(*names, **kwargs):
"""
autoassign(function) -> method
autoassign(*argnames) -> decorator
autoassign(exclude=argnames) -> decorator
allow a method to assign (some of) its arguments as attributes of
'self' automatically. E.g.
>>> class Foo(object):
... @autoassign
... def __init__(self, foo, bar): pass
...
>>> breakfast = Foo('spam', 'eggs')
>>> breakfast.foo, breakfast.bar
('spam', 'eggs')
To restrict autoassignment to 'bar' and 'baz', write:
@autoassign('bar', 'baz')
def method(self, foo, bar, baz): ...
To prevent 'foo' and 'baz' from being autoassigned, use:
@autoassign(exclude=('foo', 'baz'))
def method(self, foo, bar, baz): ...
"""
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 from fargnames and make sure fdefault 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()))
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
#---------- Examples of use ------------------
>>> class Test(object):
... @autoassign('foo', 'bar')
... def __init__(self, foo, bar=3, baz=6):
... "some clever stuff going on here"
... 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)
baz = 5
>>> u = Test(foo=8)
baz = 6
>>> v = Test2(10, 11)
>>> w = Test3(100, 101, foobar=102)
>>>
>>> print Test.__init__.__doc__
some clever stuff going on here
>>>
>>> print t.foo
1
>>> print t.bar
2
>>>
>>> print u.foo
8
>>> print u.bar
3
>>>
>>> print v.foo, v.bar # 10 11
10 11
>>> print w.baz, w.foobar # 5 102
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)
...
w.foo raises AttributeError
w.bar raises AttributeError
t.baz raises AttributeError
>>>
|
This recipe was first suggested in a thread on comp.lang.python:
The original idea for an 'autoassign' decorator came form Diez B. Roggisch (see thread above), I modified it a bit and improved it after feedback from several people on c.l.p.
Tags: oop
Excellent. Excellent snippet and very useful too!
Excellent. Excellent snippet and very useful too!
3 more alternate versions. Here's another one: http://bigasterisk.com/darcs/?r=waterworks;a=headblob;f=/waterworks/Tools.py
and http://bigasterisk.com/post/makeattrs has another one:
good code i just change line 51: self.__dict__.update(assigned) with * for k,v in assigned.iteritems(): setattr(self, k, v) * because it does not work with tools like sqlalchemy