Variable arguments (*args, **kwargs): seeking elegance
Steven D'Aprano
steve+comp.lang.python at pearwood.info
Mon Oct 7 17:13:10 EDT 2013
On Mon, 07 Oct 2013 09:26:51 -0700, John Ladasky wrote:
> Thanks, everyone, for your replies. Perhaps I have complicated things
> unnecessarily? I was just trying to do some error-checking on the
> arguments supplied to the class constructor. Perhaps Python already
> implements automatically what I am trying to accomplish manually? I'll
> tinker around with some minimal code, try to provoke some errors, and
> see what I get.
It's really hard to make definitive judgements without actually seeing
your code and understanding your use-case. I can only suggest that, you
*may* be complicating things unnecessarily. On the other hand, there's
always the chance that your requirements are sufficiently unusual that
you have done exactly what needs to be done.
But I suspect even in this case, there may be a more elegant way to solve
the problem of "I'm finding it to be a bit clunky", to quote your
original post. Clunky code can sometimes be smoothed out by refactoring
the complexity by use of decorators. Can you post an example of your code?
One thought -- often, people turn to subclassing as the only tool in
their toolbox. Have you considered that it may be easier/better to work
with delegation and composition instead?
> Here is one more detail which may be relevant. The base class for the
> family of classes I am developing is a numpy.ndarray. The numpy.ndarray
> is a C extension type (and if I understand correctly, that means it is
> immutable by ordinary Python methods). Subclassing ndarray can get a
> bit complicated (see
> http://docs.scipy.org/doc/numpy/user/basics.subclassing.html). The
> ndarray.__init__ method is inaccessible, instead one overrides
> ndarray.__new__.
Don't forget ndarray.__array_finalize__, __array_wrap__ and
__array_prepare__.
I am not an expert on numpy, but reading that page just makes me think
they're doing it all wrong, adding far too much complication. (I've
written code like that myself, but thank goodness I've had the sense to
throw it away and start again...). I'm trying to give them the benefit of
the doubt, but I've never liked the amount of DWIM cleverness in numpy,
and I think they would have been *much* better off having a clean
separation between the three ways of creating an array:
- the normal Python __new__ and __init__ mechanism
- creating a view into an array
- templating
instead of conflating the three into a single mechanism. I suspect that
the fundamental confusion comes about because numpy doesn't have a clean
distinction between views into an array, and actual arrays. Although I
must admit I've not done more than dip my toe into numpy, so you should
take my criticisms with a generous pinch of salt.
> Making further subclasses of a subclassed numpy.ndarray, each of which
> may have their own arguments, is what I am trying to accomplish while
> adhering to the "DRY" principle.
The usual way of doing this is to accept only keyword arguments for any
additional args:
class Base:
def __new__(cls, doc, grumpy, happy, sleepy, bashful, sneezy, dopey):
...
class Subclass(Base):
def __new__(cls, *args, **kwargs):
# grab the additional arguments
sneaky = kwargs.pop('sneaky', True) # optional
grabby = kwargs.pop('grabby') # mandatory
touchy = kwargs.pop('touchy')
feely = kwargs.pop('feely')
instance = super(Subclass, cls).__new__(cls, *args, **kwargs)
# process additional arguments
instance.apply_extras(sneaky, grabby, touchy, feely)
return instance
# In Python 3, I can do this to make it even cleaner:
class Subclass(Base):
def __new__(cls, *args, sneaky=True, grabby, touchy, feely, **kwargs):
instance = super(Subclass, cls).__new__(cls, *args, **kwargs)
# process additional arguments
instance.apply_extras(sneaky, grabby, touchy, feely)
return instance
In general, you should aim to use either __new__ or __init__ but not
both, although that's not a hard law, just a guideline.
Can you adapt this pattern to ndarray?
--
Steven
More information about the Python-list
mailing list