carry **arguments through different scopes/functions

Peter Otten __peter__ at web.de
Sun Jan 31 08:49:56 EST 2016


c.buhtz at 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`.
> 
> 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`.

The solution that requires the least changes to your code is probably to 
turn return_code into an attribute of a mutable argument (called context 
below):

#!/usr/bin/env python3
import sys
import types


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, context):
    if context.return_code:
        return

    if one_id == 7:
        context.return_code = True

    print('one_id: {}\treturn_code: {}'.format(one_id, context.return_code))


def _isSevenInIt(ids):
    context = types.SimpleNamespace(return_code=False)

    walkOn_ids(ids=ids,
               handlerFunction=_on_id,
               context=context)

    return context.return_code


ids = [1, 2, 3, 4, 5, 6, 8, 9]  # NO 7
print(ids)

if _isSevenInIt(ids):
    print('7 is in')
else:
    print('no 7 in it')

However, the resulting code is not very pythonic.

In idiomatic Python the toy problem you present -- looking for the first 
item in an iterable that fulfills a certain condition -- would be solved 
like that:

def equals_seven(id):
    return id == 7

ids = [1, 2, 3, 4, 5, 6, 8, 9]  # NO 7

if any(equals_seven(id) for id in ids):
    print('7 is in')
else:
    print('no 7 in it')

It is likely that your actual code can be simplified, too.




More information about the Python-list mailing list