8

In python, is there a way I can use instance variables as optional arguments in a class method? ie:

def function(self, arg1=val1, arg2=val2, arg3=self.instance_var):
    # do stuff....

Any help would be appreciated.

5 Answers5

16

Try this:

def foo(self, blah=None):
    if blah is None: # faster than blah == None - thanks to kcwu
        blah = self.instance_var
Unknown
  • 45,913
  • 27
  • 138
  • 182
  • 4
    use "is" to compare None is better (and faster) – kcwu May 15 '09 at 05:49
  • @kcwu thanks for the suggestion. I knew they were different, but I did a benchmark and found a large performance difference. – Unknown May 15 '09 at 05:59
  • @kcwu hmm this is very interesting, using "is" to compare strings also shows it is 50% faster. However, this does not work with integers. – Unknown May 15 '09 at 06:07
  • Don't compare string with "is" except you really want to compare the reference instead of value. – kcwu May 15 '09 at 06:12
  • @kcwu but strings are unique, therefore when you do something like "a"+"s"+"d"+"f" is "asdf" then it is true. – Unknown May 15 '09 at 07:01
  • 1
    @Unknown - no not always. Python interns some strings depending on how they are created etc, but not all. Try ('%s%s' %('x','y')) is ('%s%s' % ('x','y')) instead, or reading two identical strings from a file. – Brian May 15 '09 at 07:45
  • @Brian hmm ok, I've never seen anything about that even though it runs counter to all the documentation I have read. – Unknown May 15 '09 at 08:07
  • @Unknown: You may be confusing strings being unique with them being immutable. This means that they can't be changed, so it's a valid optimisation for the same strings to be represented by the same object, but you can't rely on it. It's true only for strings which are interned. Python automatically does this for short strings that might be an identifier, as this will improve performance for dict and variable lookups etc. Checking every string on creation for duplicates is expensive though, so it's not done for all strings (since there's no gain if it's never compared). – Brian May 15 '09 at 16:38
  • You can also use other constants as sentinels like `False`, `True`, `NotImplemented`. – Shane Holloway Jan 26 '10 at 20:32
  • Is `if not blah` as fast as `if blah is None`? – zzz Mar 28 '12 at 20:55
5

All the responses suggesting None are correct; if you want to make sure a caller can pass None as a regular argument, use a special sentinel and test with is:

class Foo(object):
  __default = object()
  def foo(self, blah=Foo.__default):
    if blah is Foo.__default: blah = self.instavar

Each call to object() makes a unique object, such that is will never succeed between it and any other value. The two underscores in __default mean "strongly private", meaning that callers know they shouldn't try to mess with it (and would need quite some work to do so, explicitly imitating the name mangling that the compiler is doing).

The reason you can't just use the code you posted, btw, is that default values evaluate when the def statement evaluates, not later at call time; and at the time def evaluates, there is as yet no self from which to take the instance variable.

Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
2

no, because the instance doesn't exist when class function definition time

You have to rewrite as following

def function(self, arg1=val1, arg2=val2, arg3=None):
    if arg3 is None:
        arg3 = self.instance_var

This is slightly different to original one: you cannot pass arg3 with None value if you really want.

Alternative solution:

def function(self, arg1=val1, arg2=val2, **argd):
    arg3 = argd.get('arg3', self.instance_var)
kcwu
  • 6,778
  • 4
  • 27
  • 32
0
def foo(self, blah=None):
    blah = blah if not blah is None else self.instance_var

This works with python 2.5 and forwards and handles the cases where blah is empty strings, lists and so on.

vogonistic
  • 309
  • 1
  • 6
-1

An alternative way of doing this would be:

def foo(self, blah=None):
    blah = blah or self.instance_var

This shorter version looks better, specially when there is more than one optional argument.

Use with care. See the comments below...

Sergio
  • 4,537
  • 4
  • 33
  • 41