Tell me more ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I am trying to create a script that sets a local variable, references it from a function, and can return the manipulated value back to the main scope (or whatever it's called; I'm new to Python)

I have simplified my code to show the utmost basics of what I am trying to accomplish, which is to import a local from the module into a function block, I think.

I have gotten this to work by using globals, but that isn't the best solution . . .

chambersinreactor = 0;
cardsdiscarded = 0;

def find_chamber_discard(): 
    """Find chambers and discard in row (reads each player slot)"""
    chambersinreactor = 0; # Resets the variable, not what I want
    cardsdiscarded = 0; # Resets the variable, not what I want
    chambersinreactor += 1
    cardsdiscarded += 1
    return # Don't know what to put here

find_chamber_discard()

print chambersinreactor # prints as 0, should be 1
print cardsdiscarded    # prints as 0, should be 1
share|improve this question
Do you mean you want to pass in arguments to the function then return them? – Greg Brown May 11 '12 at 3:33
1  
chambersinreactor and ` cardsdiscarded` ARE global variables. They're defined outside of the local scope. – Joel Cornett May 11 '12 at 3:41

3 Answers

Functions shouldn't have to know what scope they're called from; the point of a function is to make a re-usable block of code that can be invoked multiple times from different places.

You communicate information to a function by passing it through its input variables. The function communicates information back to its caller by returning it.

Managing the variables of a scope is the job of the code in that scope not any functions it invokes. If you need to set variables to values determined by a function, then you have the function return those values and you use them to set the variables. If the values the function is calculating depend on the values of variables you have in the calling scope, then you need to pass them to the function as arguments. The function you're calling shouldn't have to know what variables you're using, and shouldn't be able to mess with them.

Putting that all together, what you want to do is something like this:

def find_chamber_discard(chambersinreactor, cardsdiscarded):
    chambersinreactor += 1
    cardsdiscarded += 1
    return (chambersinreactor, cardsdiscarded)

chambersinreactor = 0;
cardsdiscarded = 0;

chambersinreactor, cardsdiscarded = find_chamber_discard(chambersinreactor, cardsdiscarded)

print chambersinreactor
print cardsdiscarded

There are ways to get around this with global variables or manipulating mutable data structures, but ultimately they make your program less flexible and more likely to contain errors that will be difficult to spot. There is a place for those techniques, but the first method you reach for to communicate information to and from functions really should be passing arguments and receiving return values.

share|improve this answer
I'd vote up if i could :D – Zhall May 14 '12 at 3:05
#!/usr/bin/env python
chambersinreactor = 0; cardsdiscarded = 0;

def find_chamber_discard():
    chambersinreactor = 0
    cardsdiscarded = 0
    chambersinreactor += 1 
    cardsdiscarded += 1 
    return(chambersinreactor, cardsdiscarded)

#Here the globals remain unchanged by the locals.
#In python, a scope is similar to a 'namespace'
find_chamber_discard()

print chambersinreactor #prints as 0
print cardsdiscarded 

#I've modified the function to return a pair (tuple) of numbers.
#Above I ignored them. Now I'm going to assign the variables in the 
#main name space to the result of the funtion call.
print("=====with assignment===")
(chambersinreactor, cardsdiscarded) = find_chamber_discard()

print chambersinreactor #  now prints as 1
print cardsdiscarded 

# Here is another way that doesn't depend on returning values.
#Pass a dictionary of all the main variables into your function
#and directly access them from within the fuction as needed

print("=======using locals===")
def find_chamber_discard2(_locals):
    _locals['chambersinreactor'] += 1
    _locals['cardsdiscarded'] += 1
    return

find_chamber_discard2(locals())

print chambersinreactor #incremented the value, which was already 1
print cardsdiscarded 
share|improve this answer
The recommendation from the docs is to never modify locals(), as the behaviour you're noting is entirely based on the implementation and is not guaranteed: "The contents of this dictionary should not be modified; changes may not affect the values of local and free variables used by the interpreter." – Matthew Trevor May 11 '12 at 5:47

One approach is to use mutable values, like dicts or lists:

settings = dict(
    chambersinreactor = 0,
    cardsdiscarded = 0
)

def find_chamber_discard():
    settings['chambersinreactor'] += 1
    settings['cardsdiscarded'] += 1

find_chamber_discard()

print settings['chambersinreactor']
print settings['cardsdiscarded']

However, if you have a function that is changing some state, you're probably better off wrapping that all up in class, as that's what they're for:

class CardCounter(object):
    def __init__(self):
        chambersinreactor = 0
        cardsdiscarded = 0

    def find_chamber_discard(self, hand):
        for card in hand:
            if card.is_chamber:
                self.chambersinreactor += 1
            if card.is_discarded:
                self.cardsdiscarded += 1

If what you're doing is counting, maybe you could use Counter:

from collections import Counter

def test_for_chamberness(x): return x == 'C'
def test_for_discarded(x): return x == 'D'

def chamber_or_discard(card):
    if test_for_chamberness(card):
        return 'chambersinreactor'
    if test_for_discarded(card):
        return 'cardsdiscarded'

hand = ['C','C','D','X','D','E','C']

print Counter(
    x for x in (chamber_or_discard(card) for card in hand) if x is not None
)

Personally, I'd go for the class approach, perhaps even wrapping Counter, as it keeps all the associated functionality together.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.