Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.lang.python > #69605 > unrolled thread

Default mutable parameters in functions

Started byfbicknel@gmail.com
First post2014-04-03 11:49 -0700
Last post2014-04-04 22:21 -0400
Articles 11 — 10 participants

Back to article view | Back to comp.lang.python


Contents

  Default mutable parameters in functions fbicknel@gmail.com - 2014-04-03 11:49 -0700
    Re: Default mutable parameters in functions Ian Kelly <ian.g.kelly@gmail.com> - 2014-04-03 15:32 -0600
    Re: Default mutable parameters in functions Ethan Furman <ethan@stoneleaf.us> - 2014-04-03 14:44 -0700
    Re: Default mutable parameters in functions Dennis Lee Bieber <wlfraed@ix.netcom.com> - 2014-04-03 20:27 -0400
    Re: Default mutable parameters in functions Mark Lawrence <breamoreboy@yahoo.co.uk> - 2014-04-04 01:38 +0100
    Re: Default mutable parameters in functions random832@fastmail.us - 2014-04-04 10:00 -0400
      Re: Default mutable parameters in functions Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-04-04 14:34 +0000
        Re: Default mutable parameters in functions Chris Angelico <rosuav@gmail.com> - 2014-04-05 09:47 +1100
    Re: Default mutable parameters in functions Dennis Lee Bieber <wlfraed@ix.netcom.com> - 2014-04-04 19:23 -0400
      Re: Default mutable parameters in functions Roy Smith <roy@panix.com> - 2014-04-04 19:38 -0400
    Re: Default mutable parameters in functions Dave Angel <davea@davea.name> - 2014-04-04 22:21 -0400

#69605 — Default mutable parameters in functions

Fromfbicknel@gmail.com
Date2014-04-03 11:49 -0700
SubjectDefault mutable parameters in functions
Message-ID<ac388f0b-8951-42af-a94f-33abdb7231e7@googlegroups.com>
Hi all,

So I was reading about default values for functions and spied this:

Important warning: The default value is evaluated only once. This makes a difference when the default is a mutable object such as a list, dictionary, or instances of most classes. 

That's ok - guess I can get used to that.  So I did some experimenting and sure enough, immutables sort of behave "normally" in that they get the default value every time you call the function and don't specify the value for the parameter.

But mutables behave very strangely (imho).  Take this example:

def foo(bar=[ 42 ]):
    print "It's a parrot, not a cheese: {value}".format(value=bar)
    bar[0] += 1

Now call it like this:
foo()
foo()
foo()
foo()

and based on the "Important warning" above, you get something expected:
It's a parrot, not a cheese: [42]
It's a parrot, not a cheese: [43]
It's a parrot, not a cheese: [44]
It's a parrot, not a cheese: [45]

Now call it with a value:
foo([ 3 ])

as you might expect:
It's a parrot, not a cheese: [3]

But now go back to no parameter in the call:
foo()
foo()
foo()

It's a parrot, not a cheese: [46]
It's a parrot, not a cheese: [47]
It's a parrot, not a cheese: [48]

it picks up where it left off.

I was rather expecting it to start with 4!

I put this into pythontutor.com's code visualization tool (http://goo.gl/XOmMjR) and it makes more sense what's going on.

I thought this was interesting; thought I would share.

[toc] | [next] | [standalone]


#69612

FromIan Kelly <ian.g.kelly@gmail.com>
Date2014-04-03 15:32 -0600
Message-ID<mailman.8857.1396560805.18130.python-list@python.org>
In reply to#69605
On Thu, Apr 3, 2014 at 12:49 PM,  <fbicknel@gmail.com> wrote:
> Now call it with a value:
> foo([ 3 ])
>
> as you might expect:
> It's a parrot, not a cheese: [3]
>
> But now go back to no parameter in the call:
> foo()
> foo()
> foo()
>
> It's a parrot, not a cheese: [46]
> It's a parrot, not a cheese: [47]
> It's a parrot, not a cheese: [48]
>
> it picks up where it left off.
>
> I was rather expecting it to start with 4!

You haven't replaced the default value; you've only substituted a
different value for that call.  In this function:

def foo(x=3):
    print(x)

You wouldn't expect a call of foo(27) to change the default value to
27, would you?

[toc] | [prev] | [next] | [standalone]


#69613

FromEthan Furman <ethan@stoneleaf.us>
Date2014-04-03 14:44 -0700
Message-ID<mailman.8858.1396561500.18130.python-list@python.org>
In reply to#69605
On 04/03/2014 11:49 AM, fbicknel@gmail.com wrote:
>
> I put this into pythontutor.com's code visualization tool (http://goo.gl/XOmMjR) and it makes more sense what's going on.
>
> I thought this was interesting; thought I would share.

That visualization tool is certainly neat, thanks!

--
~Ethan~

[toc] | [prev] | [next] | [standalone]


#69624

FromDennis Lee Bieber <wlfraed@ix.netcom.com>
Date2014-04-03 20:27 -0400
Message-ID<mailman.8869.1396571282.18130.python-list@python.org>
In reply to#69605
On Thu, 3 Apr 2014 11:49:56 -0700 (PDT), fbicknel@gmail.com declaimed the
following:

>But mutables behave very strangely (imho).  Take this example:
>
	No, they don't...


	<snip>

>it picks up where it left off.
>
	As it should...

>I was rather expecting it to start with 4!

>>> def foo(bar = [42]):
... 	print "I'm %s with %s" % (id(bar), bar[0])
... 	bar[0] += 1
... 	
>>> foo()
I'm 70238984 with 42
>>> foo()
I'm 70238984 with 43
>>> foo()
I'm 70238984 with 44
>>> foo([3])
I'm 70242184 with 3
>>> foo([3])
I'm 70668424 with 3
>>> foo()
I'm 70238984 with 45
>>> 

	The default was evaluated once -- it is a list with ID 70238984 (in
this case), and that ID will not change regardless of what goes on inside
the list.

	When called with [3], the function acts on a different list created
just for that call (since it was a list literal).

-- 
	Wulfraed                 Dennis Lee Bieber         AF6VN
    wlfraed@ix.netcom.com    HTTP://wlfraed.home.netcom.com/

[toc] | [prev] | [next] | [standalone]


#69625

FromMark Lawrence <breamoreboy@yahoo.co.uk>
Date2014-04-04 01:38 +0100
Message-ID<mailman.8870.1396571932.18130.python-list@python.org>
In reply to#69605
On 03/04/2014 19:49, fbicknel@gmail.com wrote:
> Hi all,
>
> So I was reading about default values for functions and spied this:

[snip]

>
> I was rather expecting it to start with 4!

I just wish I had a quid for every time somebody expects something out 
of Python, that way I'd have retired years ago.  At least here it's not 
accompanied by "as that's how it works in <some other language>".

-- 
My fellow Pythonistas, ask not what our language can do for you, ask 
what you can do for our language.

Mark Lawrence

---
This email is free from viruses and malware because avast! Antivirus protection is active.
http://www.avast.com

[toc] | [prev] | [next] | [standalone]


#69668

Fromrandom832@fastmail.us
Date2014-04-04 10:00 -0400
Message-ID<mailman.8893.1396620027.18130.python-list@python.org>
In reply to#69605
On Thu, Apr 3, 2014, at 20:38, Mark Lawrence wrote:
> I just wish I had a quid for every time somebody expects something out 
> of Python, that way I'd have retired years ago.  At least here it's not 
> accompanied by "as that's how it works in <some other language>".

I can't imagine a language that would work that way. For one, it would
also imply that passing a value would change the default for future
calls even for non-mutable types.

[toc] | [prev] | [next] | [standalone]


#69670

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2014-04-04 14:34 +0000
Message-ID<533ec2ec$0$29993$c3e8da3$5496439d@news.astraweb.com>
In reply to#69668
On Fri, 04 Apr 2014 10:00:25 -0400, random832 wrote:

> On Thu, Apr 3, 2014, at 20:38, Mark Lawrence wrote:
>> I just wish I had a quid for every time somebody expects something out
>> of Python, that way I'd have retired years ago.  At least here it's not
>> accompanied by "as that's how it works in <some other language>".
> 
> I can't imagine a language that would work that way. 

That seems like a failure of imagination to me. At least, I can't imagine 
anyone unable to imagine a language like that :-P


> For one, it would
> also imply that passing a value would change the default for future
> calls even for non-mutable types.

Not all programming languages distinguish between mutable and non-mutable 
types. Or for that matter even have types.

But it's not hard to get that effect in Python, mutable or immutable 
doesn't matter:


py> def spam(count, food="spam"):
...     spam.__defaults__ = (food,)
...     return food*count
...
py> spam(5)
'spamspamspamspamspam'
py> spam(3, 'eggs')
'eggseggseggs'
py> spam(5)
'eggseggseggseggseggs'
py> spam(5, 3)
15
py> spam(4)
12


Is it so unusual for a function to want to store persistent state which 
survives from one call to another but may also vary from time to time? 
Managing situations like that is one of the reasons OOP was invented!




-- 
Steven D'Aprano
http://import-that.dreamwidth.org/

[toc] | [prev] | [next] | [standalone]


#69686

FromChris Angelico <rosuav@gmail.com>
Date2014-04-05 09:47 +1100
Message-ID<mailman.8904.1396651644.18130.python-list@python.org>
In reply to#69670
On Sat, Apr 5, 2014 at 1:34 AM, Steven D'Aprano
<steve+comp.lang.python@pearwood.info> wrote:
> But it's not hard to get that effect in Python, mutable or immutable
> doesn't matter:
>
>
> py> def spam(count, food="spam"):
> ...     spam.__defaults__ = (food,)
> ...     return food*count
> ...
> py> spam(5)
> 'spamspamspamspamspam'
> py> spam(3, 'eggs')
> 'eggseggseggs'
> py> spam(5)
> 'eggseggseggseggseggs'
> py> spam(5, 3)
> 15
> py> spam(4)
> 12
>
>
> Is it so unusual for a function to want to store persistent state which
> survives from one call to another but may also vary from time to time?
> Managing situations like that is one of the reasons OOP was invented!

Maybe it'd be clearer if we change the names around.

def signal(sig, handler=None):
    ret = signals[sig]
    if handler:
        signals[sig] = handler
    return ret

This neatly merges the definitions of signal.signal() and
signal.getsignal(); per the docs:

"""
signal.getsignal(signalnum)

Return the current signal handler for the signal signalnum. The
returned value may be a callable Python object, or one of the special
values signal.SIG_IGN, signal.SIG_DFL or None. Here, signal.SIG_IGN
means that the signal was previously ignored, signal.SIG_DFL means
that the default way of handling the signal was previously in use, and
None means that the previous signal handler was not installed from
Python.

signal.signal(signalnum, handler)

Set the handler for signal signalnum to the function handler. handler
can be a callable Python object taking two arguments (see below), or
one of the special values signal.SIG_IGN or signal.SIG_DFL. The
previous signal handler will be returned (see the description of
getsignal() above). (See the Unix man page signal(2).)
"""

(Incidentally, SIG_IGN and SIG_DFL are just integers. Are they targets
for enumification?)

>>> signal(SIGINT, ctrl_c_handler)
--> whatever the default is
>>> signal(SIGINT)
ctrl_c_handler
>>> signal(SIGINT, other_ctrl_c_handler)
ctrl_c_handler
>>> signal(SIGINT)
other_ctrl_c_handler


I think that's a perfectly reasonable API... it just doesn't happen to
be how Python works by default.

ChrisA

[toc] | [prev] | [next] | [standalone]


#69692

FromDennis Lee Bieber <wlfraed@ix.netcom.com>
Date2014-04-04 19:23 -0400
Message-ID<mailman.8908.1396653807.18130.python-list@python.org>
In reply to#69605
On Fri, 04 Apr 2014 10:00:25 -0400, random832@fastmail.us declaimed the
following:

>
>I can't imagine a language that would work that way. For one, it would
>also imply that passing a value would change the default for future
>calls even for non-mutable types.

	Some early FORTRAN compilers purportedly had problems with, for
example:

	X = 1
	call mutant(1)
	Y = 1
	
where

	subroutine mutant(y)

	y = y + 1
	return

meant that Y now held the value of 2 -- that is, literals were stored in
mutable memory, and since FORTRAN passes by reference, the address of the
literal is passed, and the assignment changed the "constant".
-- 
	Wulfraed                 Dennis Lee Bieber         AF6VN
    wlfraed@ix.netcom.com    HTTP://wlfraed.home.netcom.com/

[toc] | [prev] | [next] | [standalone]


#69693

FromRoy Smith <roy@panix.com>
Date2014-04-04 19:38 -0400
Message-ID<roy-8A8A10.19381904042014@news.panix.com>
In reply to#69692
In article <mailman.8908.1396653807.18130.python-list@python.org>,
 Dennis Lee Bieber <wlfraed@ix.netcom.com> wrote:

> On Fri, 04 Apr 2014 10:00:25 -0400, random832@fastmail.us declaimed the
> following:
> 
> >
> >I can't imagine a language that would work that way. For one, it would
> >also imply that passing a value would change the default for future
> >calls even for non-mutable types.
> 
> 	Some early FORTRAN compilers purportedly had problems with, for
> example:
> 
> 	X = 1
> 	call mutant(1)
> 	Y = 1
> 	
> where
> 
> 	subroutine mutant(y)
> 
> 	y = y + 1
> 	return
> 
> meant that Y now held the value of 2 -- that is, literals were stored in
> mutable memory, and since FORTRAN passes by reference, the address of the
> literal is passed, and the assignment changed the "constant".

Problem?  I always assumed it was a feature :-)

[toc] | [prev] | [next] | [standalone]


#69697

FromDave Angel <davea@davea.name>
Date2014-04-04 22:21 -0400
Message-ID<mailman.8910.1396664147.18130.python-list@python.org>
In reply to#69605
Dennis Lee Bieber <wlfraed@ix.netcom.com> Wrote in message:
> On Fri, 04 Apr 2014 10:00:25 -0400, random832@fastmail.us declaimed the
> following:
> 
>>
>>I can't imagine a language that would work that way. For one, it would
>>also imply that passing a value would change the default for future
>>calls even for non-mutable types.
> 
> 	Some early FORTRAN compilers purportedly had problems with, for
> example:
> 
> 	X = 1
> 	call mutant(1)
> 	Y = 1
> 	
> where
> 
> 	subroutine mutant(y)
> 
> 	y = y + 1
> 	return
> 
> meant that Y now held the value of 2 -- that is, literals were stored in
> mutable memory, and since FORTRAN passes by reference, the address of the
> literal is passed, and the assignment changed the "constant".

I can confirm that,  first hand.

In late 60's, CDC 6400, I deliberately wrote code to exploit that.
  Not for production of course.


-- 
DaveA

[toc] | [prev] | [standalone]


Back to top | Article view | comp.lang.python


csiph-web