-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathautoassign.py
54 lines (47 loc) · 1.86 KB
/
autoassign.py
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
from functools import wraps
from inspect import getfullargspec, isfunction
from itertools import 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:filter(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: filter(lambda nv: nv[0] in names, l)
def decorator(f):
fargnames, _, _, fdefaults, _, _, _ = getfullargspec(f)
# Remove self from fargnames and make sure fdefault is a tuple
fargnames, fdefaults = fargnames[1:], fdefaults or ()
defaults = list(sieve(zip(reversed(fargnames), reversed(fdefaults))))
@wraps(f)
def decorated(self, *args, **kwargs):
assigned = dict(sieve(zip(fargnames, args)))
assigned.update(sieve(kwargs.items()))
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