2

Let's say I want to set the default value of an argument of a method of a class to the value of some data attribute of the class. What is an appropriate way to do this? Is there a way to do this using lambda functions?

Here is some example code:

class A(object):

    def __init__(
        self
        ):
        self._defaultReportText = "hello world"

    def report(
        self,
        text = self._defaultReportText
        ):
        return(text)

Obviously, the default value specification for the argument text of the method report does not work here because self is not defined at this point. How should I make this value available?

d3pd
  • 7,935
  • 24
  • 76
  • 127
  • just note that `self._defaultReportText` [isn't a class attribute, but an instance attribute](http://stackoverflow.com/questions/207000/python-difference-between-class-and-instance-attributes). – Roberto Dec 28 '14 at 02:09

3 Answers3

7

If I understood your question, I usually do it with:

def report(self, text = None):
    if text is None:
        text = self._defaultReportText
    return(text)

(Edit: changed if not text to if text is None after Warren Weckesser's suggestion.)

Roberto
  • 2,696
  • 18
  • 31
4

You cannot set the default value at class definition time, since defaults are stored with the function object, so no instances are available yet to take the default from.

Instead, use a sentinel, a unique value that you can easily test for. Usually this is None:

class A(object):
    def __init__(self):
        self._defaultReportText = "hello world"

    def report(self, text=None):
        if text is None:
            text = self._defaultReportText
        return text

If no text argument was supplied, it defaults to None and that can easily be tested for.

If None should be a possible value (e.g. you want to be able to use A().report(None) and that would behave differently from A().report(), then you'll have to pick a different sentinel. In that case use a unique instance of object() to test against:

_sentinel = object()


class A(object):
    def __init__(self):
        self._defaultReportText = "hello world"

    def report(self, text=_sentinel):
        if text is _sentinel:
            text = self._defaultReportText
        return text
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • 1
    Thank you @Martijn-Pieters, your answers are always very insightful! – Roberto Dec 28 '14 at 02:21
  • Thank you very much for your help on that. Your approach was explained very clearly. I liked your abstract approach to the sentinel. – d3pd Dec 28 '14 at 02:52
-1

An alternative to using a special value such as None is to use a class variable:

class A(object):
    _defaultReportText = "hello world"

    def report(self, text=_defaultReportText):
        return text
mhawke
  • 84,695
  • 9
  • 117
  • 138
  • The default is still fixed in the function, changing `A._defaultReportText` will not change the function default. The code only works because `_defaultReportText` is still a *local* variable when the class body is executed. – Martijn Pieters Dec 28 '14 at 02:25
  • @MartijnPieters - Yes, that's right, that's the desired effect. You don't want the function default changed anyway - the default is hard coded in both OP and your `__init__()`. OP doesn't require that it be mutable. I don't see that there is any practical difference? If I am misunderstanding your point, could you please explain in more detail? – mhawke Dec 28 '14 at 02:34
  • To, the variable may be hardcoded in the `__init__` but it still can be changed. By using a sentinel the function automatically picks up that change. – Martijn Pieters Dec 28 '14 at 02:37
  • 1
    There is no point in setting a class attribute here; you could just use `def report(self, text="hello world"):` with the exact same results, the value is bound to the function object (in `A.report.__defaults__`) either way. – Martijn Pieters Dec 28 '14 at 02:38
  • (and I wasn't being clear, the attribute on the instance can be changed later on, regardless as to what the `__init__` method set it to). – Martijn Pieters Dec 28 '14 at 02:45
  • @MartijnPieters - where is the requirement that it be mutable? It can be changed, but the OP has marked the variable private with a leading underscore to suggest that it's value _shouldn't_ be changed anyway. – mhawke Dec 28 '14 at 02:48
  • 1
    No, the attribute is marked as *internal to the implementation*, that doesn't mean it shouldn't be changed, it shouldn't be relied upon by code outside of the API. That's something entirely different. – Martijn Pieters Dec 28 '14 at 02:50
  • @MartijnPieters - Your point about just using the default argument directly in the function definition is valid. I was thinking that the default might be used in multiple methods - making it a class attribute facilitates that (but there's no OP requirement for that). Thanks for the explanation. – mhawke Dec 28 '14 at 02:55