On Fri, 24 Jul 2009 19:50:54 -0700, Dr. Phillip M. Feldman wrote: > Here's a simple-minded example: > > def dumbfunc(xs): > for x in xs: > print x > > This function works fine if xs is a list of floats, but not if it is > single float. It can be made to work as follows: > > def dumbfunc(xs): > if isinstance(xs,(int,float,complex)): > xs= [xs] > for x in xs: > print x > > Having to put such extra logic into practically every function is one of > the annoying things about Python.
But it's not "practically every function". It's hardly any function at all -- in my code, I don't think I've ever wanted this behavior. I would consider it an error for function(42) and function([42]) to behave the same way. One is a scalar, and the other is a vector -- they're different things, it's poor programming practice to treat them identically. (If Matlab does this, so much the worse for Matlab, in my opinion.) However, if you want this behaviour, it's easy enough to get it with a decorator: from functools import wraps def matlab(func): """Decorate func so that it behaves like Matlab, that is, it considers a single scalar argument to be the same as a vector of length one.""" @wraps(func) def inner(arg, **kwargs): # If arg is already a vector, don't touch it. try: # If this succeeds, it's a vector of some type. iter(arg) except TypeError: # It's a scalar. arg = [arg] # Now call the decorated function (the original). return func(arg, **kwargs) return inner With this decorator in hand, you can now easily get the behaviour you want with a single extra line per function: >>> @matlab ... def mean(numbers): ... return sum(numbers)/len(numbers) ... >>> mean([4.5]) 4.5 >>> mean(4.5) 4.5 >>> mean([4.5, 3.6]) 4.0499999999999998 Decorators are extremely powerful constructs, and well worth learning. As an example, here's a similar decorator that will accept multiple arguments, like the built-in functions min() and max(): def one_or_many(func): """Decorate func so that it behaves like the built-ins min() and max().""" @wraps(func) def inner(*args, **kwargs): # If we're given a single argument, and it's a vector, # use it as args. if len(args) == 1: try: iter(args[0]) except TypeError: pass else: # No exception was raised, so it's a vector. args = args[0] # Now call the decorated function (the original). return func(args, **kwargs) return inner And then use it: >>> @one_or_many ... def minmax(numbers): ... """Return the minimum and maximum element of numbers, ... making a single pass.""" ... # the following will fail if given no arguments ... smallest = biggest = numbers[0] ... for x in numbers[1:]: ... if x < smallest: ... smallest = x ... elif x > biggest: ... biggest = x ... return (smallest, biggest) ... >>> >>> minmax([2, 4, 6, 8, 1, 3, 5, 7]) (1, 8) >>> minmax(2, 4, 6, 8, 1, 3, 5, 7) (1, 8) -- Steven -- http://mail.python.org/mailman/listinfo/python-list