function argument dependent on another function argument?

Discussion in 'Python' started by Reckoner, Jan 18, 2009.

  1. Reckoner

    Reckoner Guest

    I would like to do:

    def foo(self,x,y=self.a)

    where the default value for y=self.a. Since this is not possible, I
    wind up doing


    def foo(self,x,y=None)
    if not y:
    y=self.a

    but that seems kind of clumsy.

    Is there a better way to do this?

    Thanks in advance
     
    Reckoner, Jan 18, 2009
    #1
    1. Advertising

  2. Reckoner

    Aaron Brady Guest

    On Jan 18, 8:19 am, Reckoner <> wrote:
    > I  would like to do:
    >
    > def foo(self,x,y=self.a)
    >
    > where the default value for y=self.a. Since this is not possible, I
    > wind up doing
    >
    > def foo(self,x,y=None)
    >   if not y:
    >     y=self.a
    >
    > but that seems kind of clumsy.
    >
    > Is there a better way to do this?
    >
    > Thanks in advance


    No. The only alternative is really niche and probably not what you
    want. You'd use a decorator (which I don't have, btw):

    @defval( y= selfattrget( 'a' ) )
    def foo( self, x, y ).
     
    Aaron Brady, Jan 18, 2009
    #2
    1. Advertising

  3. On Sun, 18 Jan 2009 06:19:03 -0800, Reckoner wrote:

    > I would like to do:
    >
    > def foo(self,x,y=self.a)
    >
    > where the default value for y=self.a. Since this is not possible, I wind
    > up doing
    >
    >
    > def foo(self,x,y=None)
    > if not y:
    > y=self.a
    >
    > but that seems kind of clumsy.


    It's also incorrect, because if you pass y=0 as an argument, or any other
    false value, it will be replaced by self.a.


    > Is there a better way to do this?


    def foo(self, x, y=None):
    if y is None:
    y = self.a


    I don't find that clumsy in the least. I find it perfectly readable and a
    standard idiom.


    --
    Steven
     
    Steven D'Aprano, Jan 18, 2009
    #3
  4. Reckoner

    Paul Rubin Guest

    Steven D'Aprano <> writes:
    > def foo(self, x, y=None):
    > if y is None:
    > y = self.a
    >
    > I don't find that clumsy in the least. I find it perfectly readable and a
    > standard idiom.


    That has the same problem as the earlier version. If the person
    passes None, they get self.a. I prefer:

    sentinel = object()
    ...

    def foo(x, y=sentinel):
    if y is sentinel:
    y = self.a
     
    Paul Rubin, Jan 18, 2009
    #4
  5. Reckoner

    Aaron Brady Guest

    On Jan 18, 9:36 am, Paul Rubin <http://> wrote:
    > Steven D'Aprano <> writes:
    > > def foo(self, x, y=None):
    > >     if y is None:
    > >         y = self.a

    >
    > > I don't find that clumsy in the least. I find it perfectly readable and a
    > > standard idiom.

    >
    > That has the same problem as the earlier version.  If the person
    > passes None, they get self.a.  I prefer:
    >
    >     sentinel = object()
    >     ...
    >
    >     def foo(x, y=sentinel):
    >       if y is sentinel:
    >           y = self.a


    It is too bad that it is so much work to detect whether 'y' was passed
    in the function call directly. However, sentinel is just as good (or
    nearly); at worst, you need one sentinel per argument per function,
    which is possible to create, which has a specific meaning. If you are
    making systematic function calls, e.g. with a dictionary or list, you
    can just use the sentinel in the dictionary.
     
    Aaron Brady, Jan 18, 2009
    #5
  6. Reckoner

    Aaron Brady Guest

    On Jan 18, 10:44 am, Rob Williscroft <> wrote:
    > Aaron Brady wrote innews:6a10378f-addb-4d56-bc1b-0c382b3cb957@t26g2000prh..googlegroups.com
    > in comp.lang.python:
    >
    >
    >
    > > On Jan 18, 9:36 am, Paul Rubin <http://> wrote:
    > >> Steven D'Aprano <> writes:
    > >> > def foo(self, x, y=None):
    > >> >     if y is None:
    > >> >         y = self.a

    >
    > >> > I don't find that clumsy in the least. I find it perfectly readable
    > >> > and

    > >  a
    > >> > standard idiom.

    >
    > >> That has the same problem as the earlier version.  If the person
    > >> passes None, they get self.a.  I prefer:

    >
    > >>     sentinel = object()
    > >>     ...

    >
    > >>     def foo(x, y=sentinel):
    > >>       if y is sentinel:
    > >>           y = self.a

    >
    > > It is too bad that it is so much work to detect whether 'y' was passed
    > > in the function call directly.  However, sentinel is just as good (or
    > > nearly); at worst, you need one sentinel per argument per function,

    >
    > One per Module should be good enough. The only reason None doesen't
    > suffice is that it has other legitimate uses.  Though to be honest
    > I would always use None as the sentinel if it wasn't a legitimate
    > argument.
    >
    > > which is possible to create, which has a specific meaning.  If you are
    > > making systematic function calls, e.g. with a dictionary or list, you
    > > can just use the sentinel in the dictionary.

    >
    > IIUYC then, one sentinel is still only needed as the missing argument
    > is indicated by *both* position and value or by name and value (in the
    > case of a keyword-dictionary), so seperate distinct sentinel objects
    > aren't required, for example:
    >
    > SENTINEL = object()
    >
    > def f( a, b, c = SENTINEL, d = SENTINEL ):
    >   print( "values: %r" % ( ( a, b, c, d ), ) )
    >   if c is SENTINEL:
    >     print( "c is missing" )
    >   if d is SENTINEL:
    >     print( "d is missing" )
    >
    > f( *( 1, 2, SENTINEL, SENTINEL ) )
    >
    > f( **dict( a = 1 , b = 2, d = 4 ) )
    >
    > f( **dict( a = 1 , b = 2, d = 4, c = SENTINEL ) )
    >
    > Rob.
    > --http://www.victim-prime.dsl.pipex.com/


    I don't have a concrete example, so you may prove to be right, but I'm
    not convinced.

    If you have one function with an argument that defaults to an empty
    list, and calls another with an argument that defaults to an empty
    dict, then what is the meaning of passing sentinel to the first one?
    Whereas, if each had their own, then passing the first one's default
    would mean the empty list, and passing the second one's default would
    mean the dict.

    (Or, even if that evaluates correctly, perhaps there is no such useful
    program.)
     
    Aaron Brady, Jan 18, 2009
    #6
  7. Reckoner

    andrew cooke Guest

    >     sentinel = object()
    >     ...
    >
    >     def foo(x, y=sentinel):
    >       if y is sentinel:
    >           y = self.a


    it just struck me you could also do:

    def foo(self, x, *y_args)
    y = y_args[0] if y_args self.a

    which more directly checks whether an argument was passed, but has the
    downside of making the method signature less clear in the declaration.

    andrew
     
    andrew cooke, Jan 18, 2009
    #7
  8. On Sun, 18 Jan 2009 07:36:53 -0800, Paul Rubin wrote:

    > Steven D'Aprano <> writes:
    >> def foo(self, x, y=None):
    >> if y is None:
    >> y = self.a
    >>
    >> I don't find that clumsy in the least. I find it perfectly readable and
    >> a standard idiom.

    >
    > That has the same problem as the earlier version.


    No it doesn't. The earlier version had the problem that *any* false
    object is replaced by self.a. I'm just following the standard Python
    idiom of using None as a sentinel. Have a look through the standard
    library and see how many times it is used.

    Built-ins rarely accept None as a sentinel, slice() being a conspicuous
    exception. This is sometimes a nuisance when writing wrappers:

    def my_find(S, sub, start=None, end=None):
    """Like string.find() only with pre-processing."""
    pre_process() # stub for something complicated
    if end is None and start is None:
    return S.find(sub)
    elif end if None:
    return S.find(sub, start)
    else:
    return S.find(sub, start, end)

    or if you prefer:

    def my_find(S, sub, start=None, end=None):
    """Like string.find()"""
    pre_process()
    args = [sub]
    if start is not None:
    args.append(start)
    if end is not None:
    args.append(end)
    return S.find(*args)


    Having said that, there are times where you need to pass None as a
    legitimate argument and not as a sentinel. In that case, your solution:

    > If the person passes
    > None, they get self.a. I prefer:
    >
    > sentinel = object()
    > ...
    >
    > def foo(x, y=sentinel):
    > if y is sentinel:
    > y = self.a


    is an excellent one.


    --
    Steven
     
    Steven D'Aprano, Jan 18, 2009
    #8
  9. Reckoner

    Paul Rubin Guest

    Steven D'Aprano <> writes:
    > Having said that, there are times where you need to pass None as a
    > legitimate argument and not as a sentinel.


    I don't think it's worth trying to figure out which those times are.
    The conclusion can be wrong, or can become wrong later because of
    some faraway change in the code. I prefer using the bulletproof
    method from the beginning, whether it is needed or not.
     
    Paul Rubin, Jan 18, 2009
    #9
  10. Reckoner

    Aaron Brady Guest

    On Jan 18, 12:42 pm, andrew cooke <> wrote:
    > >     sentinel = object()
    > >     ...

    >
    > >     def foo(x, y=sentinel):
    > >       if y is sentinel:
    > >           y = self.a

    >
    > it just struck me you could also do:
    >
    >      def foo(self, x, *y_args)
    >        y = y_args[0] if y_args self.a
    >
    > which more directly checks whether an argument was passed, but has the
    > downside of making the method signature less clear in the declaration.
    >
    > andrew


    Also, if you need to change your calling signature down the line, this
    alternative really ties your hands with regard to it. You also lose
    the ability to pass 'y' by keyword.

    George Sakkis, who I only know from the NG, has a recipe that tests a
    call against a function signature to determine what arguments are
    being passed. The 'inspect' module also has the 'getargvalues'
    function, which does something similar.
     
    Aaron Brady, Jan 19, 2009
    #10
  11. Reckoner

    Aaron Brady Guest

    On Jan 18, 12:02 pm, Rob Williscroft <> wrote:
    > Aaron Brady wrote innews:582ef883-0176-4984-9521-6c1894636891@a26g2000prf..googlegroups.com
    > in comp.lang.python:
    >
    >
    >
    > > On Jan 18, 10:44 am, Rob Williscroft <> wrote:
    > >> Aaron Brady wrote
    > >> innews:6a10378f-addb-4d56-bc1b-0c382b3cb957@t26g2000prh

    > > .googlegroups.com
    > >> in comp.lang.python:

    >
    > >> > It is too bad that it is so much work to detect whether 'y' was
    > >> > passed in the function call directly.  However, sentinel is just as
    > >> > good (or nearly); at worst, you need one sentinel per argument per
    > >> > function,

    >
    > >> One per Module should be good enough. The only reason None doesen't
    > >> suffice is that it has other legitimate uses.  Though to be honest
    > >> I would always use None as the sentinel if it wasn't a legitimate
    > >> argument.

    >
    > >> > which is possible to create, which has a specific meaning.  If you
    > >> > are
    > >> > making systematic function calls, e.g. with a dictionary or list,
    > >> > you can just use the sentinel in the dictionary.

    >
    > >> IIUYC then, one sentinel is still only needed as the missing argument
    > >> is indicated by *both* position and value or by name and value (in
    > >> the case of a keyword-dictionary), so seperate distinct sentinel
    > >> objects aren't required, for example:

    >
    > >> SENTINEL = object()

    >
    > >> def f( a, b, c = SENTINEL, d = SENTINEL ):
    > >>   print( "values: %r" % ( ( a, b, c, d ), ) )
    > >>   if c is SENTINEL:
    > >>     print( "c is missing" )
    > >>   if d is SENTINEL:
    > >>     print( "d is missing" )

    >
    > >> f( *( 1, 2, SENTINEL, SENTINEL ) )

    >
    > >> f( **dict( a = 1 , b = 2, d = 4 ) )

    >
    > >> f( **dict( a = 1 , b = 2, d = 4, c = SENTINEL ) )

    > > I don't have a concrete example, so you may prove to be right, but I'm
    > > not convinced.

    >
    > I'm afraid I can't think of a use case for passing default values around
    > eiither, and I suspect if we were to come up with one, a better solution
    > that didn't involve passing default values around could be found.
    >
    > > If you have one function with an argument that defaults to an empty
    > > list, and calls another with an argument that defaults to an empty
    > > dict, then what is the meaning of passing sentinel to the first one?
    > > Whereas, if each had their own, then passing the first one's default
    > > would mean the empty list, and passing the second one's default would
    > > mean the dict.

    >
    > If you *mean* to pass an "empty list" or "empty dict"'s you should do
    > it like:
    >
    >   function_taking_list( [] )
    >   function_taking_dict( {} )
    >
    > Its when you don't (have reason to) care that you need default arguments.


    'None' isn't a valid value for many standard library functions. So,
    if you try to pass it meaning, "Whatever the default value you usually
    use is," you'll get an error. Sometimes, the functions don't even use
    a public sentinel, so if you want the default value, you either have
    to know what it is, or you can't pass anything to that parameter.
    This is usually possible, it just prevents making a uniform call to a
    function. If you need to do some calculations to determine what
    parameters you're going to pass, you're stuck testing their presence
    with 'if-else' combinations, then calling individually.
     
    Aaron Brady, Jan 19, 2009
    #11
  12. On Sun, 18 Jan 2009 22:28:04 +0000, Steven D'Aprano wrote:

    > Built-ins rarely accept None as a sentinel, slice() being a conspicuous
    > exception. This is sometimes a nuisance when writing wrappers:
    >
    > def my_find(S, sub, start=None, end=None):
    > """Like string.find() only with pre-processing.""" pre_process() #
    > stub for something complicated if end is None and start is None:
    > return S.find(sub)
    > elif end if None:
    > return S.find(sub, start)
    > else:
    > return S.find(sub, start, end)


    Typical.

    As of Python 2.6, string.find accepts None as sentinels. All my
    beautiful, beautiful code made obsolete!!! *wink*


    --
    Steven
     
    Steven D'Aprano, Jan 19, 2009
    #12
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Ben Kial
    Replies:
    1
    Views:
    720
    Eric Enright
    Nov 15, 2004
  2. Martin Vorbrodt

    argument dependent lookup

    Martin Vorbrodt, Oct 25, 2005, in forum: C++
    Replies:
    13
    Views:
    542
    Default User
    Oct 25, 2005
  3. siddhu

    Argument-Dependent Lookup

    siddhu, May 14, 2007, in forum: C++
    Replies:
    1
    Views:
    368
    Old Wolf
    May 14, 2007
  4. siddhu

    Argument-Dependent Lookup

    siddhu, May 14, 2007, in forum: C++
    Replies:
    4
    Views:
    519
    James Kanze
    May 15, 2007
  5. puzzlecracker
    Replies:
    1
    Views:
    546
    James Kanze
    Aug 7, 2008
Loading...

Share This Page