In Dr. Dobb's Python-URL I came upon this thread
proposing a change to Python (either syntax or builtin) to facilitate
this general pattern:
class Foo(object):
def __init__(self, a, b=5, c=None):
self.a = a
self.b = b
self.c = c or {}
There's two proposals, basically. One is syntax:
class Foo(object):
- def __init__(self, .a, .b=5, c=None):
- self.c = c or {}
By putting (that very little character) . in front of an argument,
you imply that it should be assigned to self. The other builtin
is:
class Foo(object):
def __init__(self, a, b=5, c=None):
adapt_init_args(self, locals())
# reassignment fixup:
self.c = c or {}
Using frame hacks, you could easily allow adapt_init_args() to
work without arguments. Someone additionally noted a recipe
that putting _ in front of the a and b to hint to
adapt_init_args which arguments should become instance variables
(though that messes up the signature).
There's lots of ways to do this. For instance, I regularly use:
class Foo(object):
def __init__(self, **kw):
for name, value in kw.items():
setattr(self, name, value)
But that can allow typos too easily. So sometimes I set defaults as
class variables, and test for hasattr(), and raise an error if an
extraneous variable is found. It also doesn't allow for positional
arguments. Or mutable defaults, since those defaults end up as class
variables, and unwittingly mutating values for all instances will mess
you up.
This is one those There's More Than One Way To Do It cases. Though
I'm not excited about the syntax proposal, it's addresses a real
problem. Clever hacks don't cut it. There's lots of them. They work
differently. You can't necessarily recognize them each right away.
They each have flaws -- some quite significant.
So far the only hack that seems decent to me (a hack that does not
exist to my knowledge) would be a decorator that works with the _
prefixes on variables (using something like **_kw to replicate the
setattr thing I show above). This should be a clever decorator
because it needs to:
- Read the signature of the enclosed function.
- Modify that signature, removing _ prefixes, so that you can do
Foo(a=10) instead of Foo(_a=10).
- Reveal the "proper" (underscore-less) signature to introspection tools.
- Allow positional calling of keyword arguments, just like normal
functions.
A decorator for this is okay -- better than most of the things people
have been proposing -- but not terribly pleasing. It's magical, but a
kind of hidden and mysterious magic. It bridges an implementation and
an external interface, but if you don't know how the decorator works
then you won't understand the disconnect.
I'm not sure what syntax I'd want. I'm not sure if syntax is feasible
here -- there's limited room in a function signature. But I think
people who say that syntax is always the wrong
answer, or that this isn't an issue, are just too set in their ways to
see the issue. Python's strength -- particularly compared to the
other dynamic languages -- is in the consistency with which people use it.
This is how Python is different from Lisp and Ruby (and
actually how it is similar to Smalltalk). I think many people
(especially the perennial skeptics in comp.lang.python)
underappreciate this.