Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.lang.python > #102350 > unrolled thread
| Started by | <c.buhtz@posteo.jp> |
|---|---|
| First post | 2016-01-31 13:19 +0100 |
| Last post | 2016-02-01 12:53 +1100 |
| Articles | 2 — 2 participants |
Back to article view | Back to comp.lang.python
carry **arguments through different scopes/functions <c.buhtz@posteo.jp> - 2016-01-31 13:19 +0100
Re: carry **arguments through different scopes/functions Steven D'Aprano <steve@pearwood.info> - 2016-02-01 12:53 +1100
| From | <c.buhtz@posteo.jp> |
|---|---|
| Date | 2016-01-31 13:19 +0100 |
| Subject | carry **arguments through different scopes/functions |
| Message-ID | <mailman.162.1454243016.2338.python-list@python.org> |
I am not sure what the problem is here, so I don't really know how I
should call the subject for that question. Please offer a better
subject.
The code below is a extrem simplified example of the original one. But
it reproduce the problem very nice. Please focus on the variable
`return_code`.
There is a `list()` of numbers without the number `7` in. The code
check if the number `7` is in and should tell that it is not in. But it
does tell me that `7 is in`. ;)
I think I didn't know some special things about scopes of variables in
Python. This might be a very good problem to learn more about that. But
I don't know on which Python topic I should focus here to find a
solution for my own.
#!/usr/bin/env python3
import sys
def walkOn_ids(ids, handlerFunction, **handlerArgs):
for one_id in ids:
handlerFunction(one_id=one_id, **handlerArgs)
print('after handler-call for id {}\t{}'
.format(one_id, handlerArgs))
def _on_id(one_id, return_code):
if return_code is False:
return
if one_id == 7:
return_code = True
else:
return_code = False
print('one_id: {}\treturn_code: {}'.format(one_id, return_code))
def _isSevenInIt(ids):
return_code = True
walkOn_ids(ids=ids,
handlerFunction=_on_id,
return_code=return_code)
return return_code
ids = [1,2,3,4,5,6,8,9] # NO 7
print(ids)
if _isSevenInIt(ids) is True:
print('7 is in')
else:
print('no 7 in it')
sys.exit()
Of course I could make `return_code` a `global` variable. But that is
not the goal. The goal is to carry this variable inside the
walker-function and bring the result back. In the original code I will
use some more complexe data structures with `**handlerArgs`.
[toc] | [next] | [standalone]
| From | Steven D'Aprano <steve@pearwood.info> |
|---|---|
| Date | 2016-02-01 12:53 +1100 |
| Message-ID | <56aebaaf$0$1604$c3e8da3$5496439d@news.astraweb.com> |
| In reply to | #102350 |
On Sun, 31 Jan 2016 11:19 pm, c.buhtz@posteo.jp wrote:
> I am not sure what the problem is here, so I don't really know how I
> should call the subject for that question. Please offer a better
> subject.
>
> The code below is a extrem simplified example of the original one. But
> it reproduce the problem very nice. Please focus on the variable
> `return_code`.
The problem with return_code is that you are trying to use it as an "output
parameter" or "call by reference" parameter. You want behaviour like this:
a = 1
b = 2
def test_output_parameter(the_var):
print(the_var)
# Set the_var by reference.
the_var = 999
test_output_parameter(a) # sets a
test_output_parameter(b) # sets b
assert (a == 999) and (b == 999)
This cannot work in Python: Python is never call by reference. If somebody
has told you that it is call by reference, they are wrong.
You can *simulate* call by reference output parameters by using a list, but
that's the wrong way to solve this problem. The right way is for your
function to return a value, which you then assign to the variable you want
to change:
a = 1
b = 2
def test_return(the_var):
print(the_var)
return 999
a = test_return(a)
b = test_return(b)
assert (a == 999) and (b == 999)
Looking at your example code, we can replace (most of it) with these six
lines:
# Version 1: best, shortest, fastest way, with no callbacks.
ids = [1,2,3,4,5,6,8,9] # NO 7
print(ids)
if 7 in ids:
print('7 is in')
else:
print('no 7 in it')
ignoring what looks like prints trying to debug the "return_code" problem.
If you really need to use a callback structure, then try something like
this, using a generator to return each individual callback result:
# Version 2: a mess -- don't do this unless you must!
def walker(ids, handlerFunction, **handlerArgs):
for one_id in ids:
print('handler-call for id {}\t{}'.format(one_id, handlerArgs))
yield handlerFunction(one_id=one_id, **handlerArgs)
def on_id_callback(one_id, return_code):
if return_code is False:
# Why are we skipping this case?
return False
return_code = (one_id == 7)
print('one_id: {}\treturn_code: {}'.format(one_id, return_code))
def seven_in_it(ids):
return_code = True
for return_code in walker(ids, on_id_callback, return_code=return_code):
# This loop runs for the side-effects!
pass
return return_code
ids = [1,2,3,4,5,6,8,9] # NO 7
print(ids)
if seven_in_it(ids):
print('7 is in')
else:
print('no 7 in it')
Running that code gives this output:
[1, 2, 3, 4, 5, 6, 8, 9]
handler-call for id 1 {'return_code': True}
one_id: 1 return_code: False
handler-call for id 2 {'return_code': True}
one_id: 2 return_code: False
handler-call for id 3 {'return_code': True}
one_id: 3 return_code: False
handler-call for id 4 {'return_code': True}
one_id: 4 return_code: False
handler-call for id 5 {'return_code': True}
one_id: 5 return_code: False
handler-call for id 6 {'return_code': True}
one_id: 6 return_code: False
handler-call for id 8 {'return_code': True}
one_id: 8 return_code: False
handler-call for id 9 {'return_code': True}
one_id: 9 return_code: False
no 7 in it
But this is truly awful code. My sympathies if you are forced to use it, my
apologies for being so blunt if you wrote it. A better way to deal with
this would be something like this:
# Version 3: better.
def verbose_equals_seven(one_id):
flag = (one_id == 7)
print('one_id: {}\treturn_code: {}'.format(one_id, flag))
return flag
ids = [1,2,3,4,5,6,8,9] # NO 7
print(ids)
if any(verbose_equals_seven(id) for id in ids):
print('7 is in')
else:
print('no 7 in it')
Running this Version 3 code gives this output:
[1, 2, 3, 4, 5, 6, 8, 9]
one_id: 1 return_code: False
one_id: 2 return_code: False
one_id: 3 return_code: False
one_id: 4 return_code: False
one_id: 5 return_code: False
one_id: 6 return_code: False
one_id: 8 return_code: False
one_id: 9 return_code: False
no 7 in it
Not very exciting. But if we try again with a list containing id=7, we see
that any() stops processing as soon as it has a success:
ids = [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
print(ids)
if any(verbose_equals_seven(id) for id in ids):
print('7 is in')
else:
print('no 7 in it')
prints this output:
[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
one_id: 9 return_code: False
one_id: 8 return_code: False
one_id: 7 return_code: True
7 is in
> In the original code I will
> use some more complexe data structures with `**handlerArgs`.
Have you tried it? If this complex data structure is *mutable*, and you
change it, that change will be seen everywhere. Remember right at the start
of my post I mentioned that you can simulate "output parameters" with a
list? This is similar: because objects are not copied when you pass them to
a function, mutations to the object will be seen no matter where you make
the mutation.
# Version 4: mutating an object instead of returning flags.
# Don't do this unless you really must. Python is not Java
# and code like this is not considered good style
class ComplexData:
def __init__(self):
self.flag = False
def __repr__(self):
msg = "<%s instance with object id %s and flag %s>"
return msg % (type(self).__name__, id(self), self.flag)
def walker(ids, handlerFunction, **handlerArgs):
for one_id in ids:
print('handler-call for id {}\t{}'.format(one_id, handlerArgs))
handlerFunction(one_id=one_id, **handlerArgs)
def on_id_callback(one_id, something_complex):
print('one_id: {}\tsomething_complex: {}'
.format(one_id, something_complex))
if something_complex.flag is False:
something_complex.flag = (one_id == 7)
def seven_in_it(ids):
data = ComplexData()
walker(ids, on_id_callback, something_complex=data)
return data.flag
ids = [9, 8, 7, 6, 5, 4, 3, 2, 1]
print(ids)
if seven_in_it(ids):
print('7 is in')
else:
print('no 7 in it')
Running version 4 of the code prints this output:
[9, 8, 7, 6, 5, 4, 3, 2, 1]
handler-call for id 9 {'something_complex': <ComplexData instance with
object id 3082144364 and flag False>}
one_id: 9 something_complex: <ComplexData instance with object id
3082144364 and flag False>
handler-call for id 8 {'something_complex': <ComplexData instance with
object id 3082144364 and flag False>}
one_id: 8 something_complex: <ComplexData instance with object id
3082144364 and flag False>
handler-call for id 7 {'something_complex': <ComplexData instance with
object id 3082144364 and flag False>}
one_id: 7 something_complex: <ComplexData instance with object id
3082144364 and flag False>
handler-call for id 6 {'something_complex': <ComplexData instance with
object id 3082144364 and flag True>}
one_id: 6 something_complex: <ComplexData instance with object id
3082144364 and flag True>
handler-call for id 5 {'something_complex': <ComplexData instance with
object id 3082144364 and flag True>}
one_id: 5 something_complex: <ComplexData instance with object id
3082144364 and flag True>
handler-call for id 4 {'something_complex': <ComplexData instance with
object id 3082144364 and flag True>}
one_id: 4 something_complex: <ComplexData instance with object id
3082144364 and flag True>
handler-call for id 3 {'something_complex': <ComplexData instance with
object id 3082144364 and flag True>}
one_id: 3 something_complex: <ComplexData instance with object id
3082144364 and flag True>
handler-call for id 2 {'something_complex': <ComplexData instance with
object id 3082144364 and flag True>}
one_id: 2 something_complex: <ComplexData instance with object id
3082144364 and flag True>
handler-call for id 1 {'something_complex': <ComplexData instance with
object id 3082144364 and flag True>}
one_id: 1 something_complex: <ComplexData instance with object id
3082144364 and flag True>
7 is in
--
Steven
[toc] | [prev] | [standalone]
Back to top | Article view | comp.lang.python
csiph-web