1

Imagine I have the following two methods:

 def function(self, my_object):
     self.things.add(my_object)

 def function_args(self, arg1, arg2, arg3 = None):
     my_object = MyObject(arg1, arg2, arg3)
     self.things.add(my_object)

I would like to write this as a single function that can be called with positional parameters:

 something.function(an_object)
 something.function(arg1, arg2, arg3)

I can see how to do it if I'm willing to always call the resulting function with keyword arguments, and I can see how to do it if I'm willing to change the signature of the second function to accept a list or tuple of the three arguments, so that each version has single argument and I can differentiate by type (which doesn't seem quite right in Python).

Can I cleanly write a single function that will allow me to call it with either set of positional arguments?

Larry Lustig
  • 49,320
  • 14
  • 110
  • 160
  • Why have the second function at all? Can't the caller construct the object? –  May 11 '12 at 16:47
  • Absolutely, and that's how I do it in my internal code. However this library will also be used by other programmers for whom the details of that internal object are needless details. I want to offer a simpler interface (while maintaining the interface that uses the pre-constructed object since there are cases in which I'll be reusing a single instance over and over). – Larry Lustig May 11 '12 at 16:51
  • 2
    Use keyword arguments. Seriously. When Guido says "explicit is better than implicit," keyword arguments are exactly the kind of thing he's talking about. – Robert Rossney May 11 '12 at 17:02
  • "Explicit is better than implicit." — PEP20 That maxim isn't an accident, and as a writer and reader of your library I'd far prefer to call one or the other. If MyObject is an implementation detail, then *really* hide it from me. – msw May 11 '12 at 17:05
  • I fully agree with @msw, but since some cases can and do arise where overloading makes life simpler, this has already been discussed. There are some potential solutions here: http://stackoverflow.com/questions/6434482/python-function-overloading – Nisan.H May 11 '12 at 17:08

4 Answers4

1

Why not set arg2 to be optional and check for it when the function is called?

 def function_args(self, arg1, arg2 = None, arg3 = None):
     if arg2 != None:
       my_object = MyObject(arg1, arg2, arg3)
       self.things.add(my_object)
     else:
       self.things.add(arg1)
Alex Bliskovsky
  • 5,973
  • 7
  • 32
  • 41
1

I wouldn't call the idea very pythonic, but something like this would work:

def fun(*args):
    obj = args[0]
    if not isinstance(obj, MyClass):
        obj = MyClass(*args)

We don't know what your API is all about, but since you mentioned object reuse in the comment, perhaps you could make it transparent to end users, like:

def fun(arg1, arg2, arg3=None):
    key = arg1 + arg2 + arg3
    if key not in cache:
        cache[key] = MyClass(arg1, arg2, arg3)
    obj = cache[key]
    ...
georg
  • 211,518
  • 52
  • 313
  • 390
0
def function(self, *args, my_object=None)
    if my_object is None:
        my_object =  MyObject(*args)
    self.things.add(my_object)

this requires you to pass my_object as a keyword argument, but IMO this is just what you want, as it gives a hint to the magic involved.

if you really do not want keyword arguments, drop it, and check len(args) for branching.

ch3ka
  • 11,792
  • 4
  • 31
  • 28
0

Can I cleanly write a single function that will allow me to call it with either set of positional arguments?

I don't think so. But here's another attempt at what comes close.

def fn(self, a, *args):
    if args:
        self.things.add(MyObject(*((a,) + args)))
    else:
        self.things.add(a)
XORcist
  • 4,288
  • 24
  • 32