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


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

Curious function argument

Started by"ast" <nomail@invalid.com>
First post2014-11-12 14:07 +0100
Last post2014-11-28 08:52 +0300
Articles 5 — 5 participants

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


Contents

  Curious function argument "ast" <nomail@invalid.com> - 2014-11-12 14:07 +0100
    Re: Curious function argument Denis McMahon <denismfmcmahon@gmail.com> - 2014-11-12 14:03 +0000
    Re: Curious function argument Dennis Lee Bieber <wlfraed@ix.netcom.com> - 2014-11-12 09:18 -0500
    Re: Curious function argument Steven D'Aprano <steve+comp.lang.python@pearwood.info> - 2014-11-15 21:39 +1100
    Re: Curious function argument Akira Li <4kir4.1i@gmail.com> - 2014-11-28 08:52 +0300

#80602 — Curious function argument

From"ast" <nomail@invalid.com>
Date2014-11-12 14:07 +0100
SubjectCurious function argument
Message-ID<54635bce$0$2384$426a74cc@news.free.fr>
Hello

I saw in a code from a previous message in this forum
a curious function argument.

def test(x=[0]):
   print(x[0])   ## Poor man's object
   x[0] += 1

>>> test()
0
>>> test()
1
>>> test()
2
>>> 

I understand that the author wants to implement a global
variable x . It would be better to write 'global x' inside the
function.

At first test() function call, it prints 0, that's OK.
But at the second call, since we dont pass any argument to test(),
x should be equal to its default value [0] (a single item list). But
it seems that Python keeps the original object whose content has
been changed to 1.

Is it a usual way to implement a global variable ?

thx




[toc] | [next] | [standalone]


#80603

FromDenis McMahon <denismfmcmahon@gmail.com>
Date2014-11-12 14:03 +0000
Message-ID<m3vpce$pm6$2@dont-email.me>
In reply to#80602
On Wed, 12 Nov 2014 14:07:14 +0100, ast wrote:

[function def with mutable default parameter]

> I understand that the author wants to implement a global variable x . It
> would be better to write 'global x' inside the function.

It may not be the case that the purpose was to implement a global 
variable, but rather to illustrate what happens when you use a mutable 
default parameter.

> At first test() function call, it prints 0, that's OK.
> But at the second call, since we dont pass any argument to test(), x
> should be equal to its default value [0] (a single item list). But it
> seems that Python keeps the original object whose content has been
> changed to 1.

This is explained in the docs:

https://docs.python.org/3/reference/compound_stmts.html#function-
definitions

Default parameter values are evaluated from left to right *when the 
function definition is executed*[1]. This means that the expression is 
evaluated once, when the function is defined, and that the same “pre-
computed” value is used for each call. This is especially important to 
understand when a default parameter is a mutable object, such as a list 
or a dictionary: if the function modifies the object (e.g. by appending 
an item to a list), the default value is in effect modified.

[1] ie when the function is 'compiled', not each time it executes.

-- 
Denis McMahon, denismfmcmahon@gmail.com

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


#80605

FromDennis Lee Bieber <wlfraed@ix.netcom.com>
Date2014-11-12 09:18 -0500
Message-ID<mailman.15721.1415801914.18130.python-list@python.org>
In reply to#80602
On Wed, 12 Nov 2014 14:07:14 +0100, "ast" <nomail@invalid.com> declaimed
the following:

>Hello
>
>I saw in a code from a previous message in this forum
>a curious function argument.
>
>def test(x=[0]):
>   print(x[0])   ## Poor man's object
>   x[0] += 1
>
>>>> test()
>0
>>>> test()
>1
>>>> test()
>2
>>>> 
>
>I understand that the author wants to implement a global
>variable x . It would be better to write 'global x' inside the
>function.
>
	No... The author implemented a mutable DEFAULT argument.

	The argument can still be overloaded by passing something else into the
function.

y = [0]
z = 234
test(y)
test(z)

	This would not work if one instead declared a global "x"
-- 
	Wulfraed                 Dennis Lee Bieber         AF6VN
    wlfraed@ix.netcom.com    HTTP://wlfraed.home.netcom.com/

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


#80815

FromSteven D'Aprano <steve+comp.lang.python@pearwood.info>
Date2014-11-15 21:39 +1100
Message-ID<54672d63$0$12983$c3e8da3$5496439d@news.astraweb.com>
In reply to#80602
ast wrote:

> Hello
> 
> I saw in a code from a previous message in this forum
> a curious function argument.
> 
> def test(x=[0]):
>    print(x[0])   ## Poor man's object
>    x[0] += 1
> 
>>>> test()
> 0
>>>> test()
> 1
>>>> test()
> 2
>>>> 
> 
> I understand that the author wants to implement a global
> variable x . It would be better to write 'global x' inside the
> function.

No, it's not a global. A better description is that it is a way of faking
static storage for a function: the function test has a "static variable" x
which is independent of any other function's x, but it can remember its
value from one function call to another.


> At first test() function call, it prints 0, that's OK.
> But at the second call, since we dont pass any argument to test(),
> x should be equal to its default value [0] (a single item list). But
> it seems that Python keeps the original object whose content has
> been changed to 1.
> 
> Is it a usual way to implement a global variable ?

No, it's unusual. If you actually want a global, use the global keyword.
Otherwise, there are often better ways to get a similar effect, such as
using a generator:

def gen():
    x = 0
    while True:
        yield x
        x += 1

it = gen()
next(it)  # returns 0
next(it)  # returns 1
next(it)  # returns 2

You can turn that into functional form like this:

import functools
func = functools.partial(next, gen())
func()  # returns 0
func()  # returns 1
func()  # returns 2



-- 
Steven

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


#81667

FromAkira Li <4kir4.1i@gmail.com>
Date2014-11-28 08:52 +0300
Message-ID<mailman.16394.1417154034.18130.python-list@python.org>
In reply to#80602
"ast" <nomail@invalid.com> writes:

> Hello
>
> I saw in a code from a previous message in this forum
> a curious function argument.
>
> def test(x=[0]):
>   print(x[0])   ## Poor man's object
>   x[0] += 1
>
>>>> test()
> 0
>>>> test()
> 1
>>>> test()
> 2
>>>>
>
> I understand that the author wants to implement a global
> variable x . It would be better to write 'global x' inside the
> function.
>
> At first test() function call, it prints 0, that's OK.
> But at the second call, since we dont pass any argument to test(),
> x should be equal to its default value [0] (a single item list). But
> it seems that Python keeps the original object whose content has
> been changed to 1.
>
> Is it a usual way to implement a global variable ?
>
It is not a global, a global would be easily available (visible) to
other functions. You can get it if you try but Python also allows you to
get private attributes, replace builtins and other things that you
should not normally do.

*"# Poor man's object"* in this context suggests that I'm the
author. The code is meant as a part of a short Python script that is
executed directly. Imagine the *test()* definition is put inside main()
function that is called when the script is run.

`def test(*, _x=[0]):..` attaches some state (_x list) to the function
*test*. It could be used as a way to emulate *nonlocal* keyword:

  def counter():
      count = 0
      def increment():
          nonlocal count
          count += 1
          return count
      return increment
  
  c = counter()
  print(c(), c()) # 0, 1
  c = counter()
  print(c(), c()) # 0, 1

an alternative is to create an object using class:

  class Counter:
      def __init__(self):
          self.count = 0
      def __call__(self):
          self.count += 1
          return self.count

  c = Counter()
  print(c(), c())
  
For this specific case, you could use `itertools.count`:

  c = itertools.count()
  print(next(c), next(c))

which could be implemented here as a generator:

  def count():
      count = 0
      while True:
         count += 1  
         yield count

  c = count()
  print(next(c), next(c))

"Objects are data with methods attached, closures are functions with data
attached." [1]

Sometimes you need to define a class to create objects, in other cases a
function is enough (and of course functions are objects too in Python).

[1]
http://stackoverflow.com/questions/13857/can-you-explain-closures-as-they-relate-to-python


--
Akira

[toc] | [prev] | [standalone]


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


csiph-web