py3k feature proposal: field auto-assignment in constructors

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
 
T

Tim Chase

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:)

One other area that was mentioned obliquely: preservation of
docstrings (and other function attributes)

I couldn't tell from your code if it attempted to publish
function attributes:

class Foo(object):
@autoassign
def __init__(self, alpha, beta, gamma):
"Do something"
pass

f = Foo(3,1,4)
print f.__init__.__doc__

I can't say I use doc-strings in __init__ methods often, and I
suspect the autoassign is only helpful most of the time for
__init__ methods, but if it's slated for the cookbook,
completeness would be worth aspiring to.

-tkc
 
A

Arnaud Delobelle

One other area that was mentioned obliquely: preservation of
docstrings (and other function attributes)

I think @wraps(...) does this (definitely copies __doc__).
 
T

Terry Reedy

| I don't like the name convention. _name already has a perfectly good
| convention: it's a private name, don't mess with it. That includes in
| function/method signatures. With your convention, _foo is public.

Since local names, including params are inaccesible outside a function, I
don't see how the convention applies. However, the underscore could just
as well go at the end of the name. There no current convention I know of
with that.

tjr
 
T

Terry Reedy

|Here's a version that
|1. does not require new syntax
|2. does not *necessarily* override the "_" prefix convention

'self_' is way too bulky and intrusive. Putting '_' at the end of the word
is nearly as easy to detect and conflicts with no convention I know of.

tjr
 
C

coldpizza

Hi,

I appreciate everyone's feedback on the topic.

Having reflected on what has been said here, I now realize that
creating more complexity is not the way to go. I would rather favor
something that relies on existing language features, something like
the default keyword argument assignment in functions.

This is probably stupid but as a noob I would have liked something
like:
def __init__( self. = host, self. = port, self. = timeout, message =
"Connected."):
pass

This is probably even more preposterous than @host, @port, but to me
it would make more sense.

I suppose the subject has exhausted itself and I am not going to
follow it up. If anyone is interested in taking it on, then please do.

Best,
coldpizza
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,162
Latest member
GertrudeMa
Top