-2

How do I load a function with some code, inside another function exactly the same way?

or

How do I load global_variables() correctly into my step1(), and step2() function without having to copy/paste it in each function?

    def global_variables():
        global txt   
        global option_a
        global option_b
        global option_c
        global next_function_a
        global next_function_b
        global next_function_c

    def step1():
        global_variables()

        txt = step1_text # fill with correct text

        option_a = step1a_text
        option_b = step1b_text
        option_c = step1c_text

        next_function_a = step1_1 # variable = call function
        next_function_b = step2 # variable = call function
        next_function_c = step5 # variable = call function

        start_step() # call step function

    def step2():
        global_variables()

        txt = step2_text # fill with correct text

        option_a = step2a_text
        option_b = step2b_text
        option_c = step2c_text

        next_function_a = game_over(step2c_text) # variable = call function
        next_function_b = step3 # variable = call function
        next_function_c = step2_1 # variable = call function

        start_step() # call step function

I expect the:

    global txt   
    global option_a
    global option_b
    global option_c
    global next_function_a
    global next_function_b
    global next_function_c

to be included inside the step1(), or step2() function, just as if I would have copy/pasted it each time manually there.

# function to start at each new step
def start_step():
    print(txt) # print story
    attempt = False

    choice = input('> ') # variable = user input prompt

    if "A" in choice or "B" in choice or "C" in choice: # if-statement is '0' in variable (choice) or 1 then
        step_end(choice) 
    else: # if-statement is other than 'A' or 'B' or 'C' then
        game_over("Man, learn to type a letter.") # call dead function and show text message

def step_end(choice):
    if choice =="A":
        print(option_a)
        next_function_a()
    elif choice =="B":
        print(option_b)
        next_function_b()
    elif choice =="C":
        print(option_c)
        next_function_c()

# function to exit program without errors by 'game-over' or 'winning' the game
def game_over(why): # function name (parameter)
    print(why) # show argument ('why')
    exit(0) # good normal exit without errors.
monogr_rigas
  • 3
  • 1
  • 5
  • 3
    NOTHING outside of a function can affect the local/global status of variables inside that function - so what you're trying to do is fundamentally impossible. Perhaps make all of these functions methods of a class, allowing you to assign to instance attributes such as `self.txt` without any prior declaration. – jasonharper May 30 '19 at 00:55
  • 2
    It looks like you don't fully understand the scoping rules in Python. The globals declared in your `global_variables` function are only known **within that function** – James May 30 '19 at 00:56
  • Are those variables in the module's scope? - the module where the functions are defined? Why do you need to declare them as `global`? – wwii May 30 '19 at 01:10
  • @wwii I declare them as `global` to use them in the `start_step` function. Let me update and include that function here for a better understanding. – monogr_rigas May 30 '19 at 01:20
  • If those variable names exist in the scope of the module where the functions are defined then you will need to make the global declarations in `step1()` **and** `step2()` for your code to work. – wwii May 30 '19 at 01:35
  • Please please please do not use global variables like this. – juanpa.arrivillaga May 30 '19 at 02:16
  • 1
    hehe @juanpa.arrivillaga I saw one of your answers when searching for a dupe or `alternative to global variables`. – wwii May 30 '19 at 02:34
  • I could reduce the code by 80 lines using one of the suggestions here and learned a ton of new stuff. Very helpful. Amazingly helpful. Thanks again everyone! – monogr_rigas Jun 19 '19 at 04:40

2 Answers2

1

as the comments suggest, you might have to global it manually but there are ways around it

you can do this:

def step(numb):
    global txt   
    global option_a
    global option_b
    global option_c
    global next_function_a
    global next_function_b
    global next_function_c
    if num == 1:
        txt = step1_text # fill with correct text

        option_a = step1a_text
        option_b = step1b_text
        option_c = step1c_text

        next_function_a = step1_1 # variable = call function
        next_function_b = step2 # variable = call function
        next_function_c = step5 # variable = call function

        start_step() # call step function
    elif numb == 2:
        txt = step2_text # fill with correct text

        option_a = step2a_text
        option_b = step2b_text
        option_c = step2c_text

        next_function_a = game_over(step2c_text) # variable = call function
        next_function_b = step3 # variable = call function
        next_function_c = step2_1 # variable = call function

this way you can write even more steps

hope it helps :)

Kenivia
  • 384
  • 1
  • 13
0

If those variable names exist in the scope of the module where the functions are defined then you will need to make the global declarations in step1() and step2() for your code to work.

Instead you could create a dictionary in the module's scope then modify that dictionary in step1() and step2()

gv_s = {'txt':None,
        'option_a':None,
        'option_b':None,
        'option_c':None,
        'next_function_a':None,
        'next_function_b':None,
        'next_function_c':None}

def step1():

    gv_s['txt'] = step1_text # fill with correct text

    gv_s['option_a'] = step1a_text
    gv_s['option_b'] = step1b_text
    gv_s['option_c'] = step1c_text

    gv_s['next_function_a'] = step1_1 # variable = call function
    gv_s['next_function_b'] = step2 # variable = call function
    gv_s['next_function_c'] = step5 # variable = call function

    start_step() # call step function

def step2():

    gv_s['txt'] = step2_text # fill with correct text

    gv_s['option_a'] = step2a_text
    gv_s['option_b'] = step2b_text
    gv_s['option_c'] = step2c_text

    gv_s['next_function_a'] = game_over(step2c_text) # variable = call function
    gv_s['next_function_b'] = step3 # variable = call function
    gv_s['next_function_c'] = step2_1 # variable = call function

    start_step() # call step function

step_end refactored to:

def step_end(choice):
    if choice =="A":
        print(gv_s['option_a'])
        gv_s['next_function_a']()
    elif choice =="B":
        print(gv_s['option_b'])
        gv_s['next_function_b']()
    elif choice =="C":
        print(gv_s['option_c'])
        gv_s['next_function_c']()

That will get it to work but still probably isn't the best way to organize your process.


Another option might be for step1 and step2 to just return a dictionary of options and assign the return value to a name in the module's namespace.

def step1():
    return {'txt':step1_text,
            'option_a':step1a_text,
            'option_b':step1b_text,
            'option_c':step1c_text,
            'next_function_a':step1_1,
            'next_function_b':step2,
            'next_function_c':step5}

def step2():
    return {'txt':step2_text,
            'option_a':step2a_text,
            'option_b':step2b_text,
            'option_c':step2c_text,
            'next_function_a':game_over(step2c_text),
            'next_function_b':step3,
            'next_function_c':step2_1}

gv_s = step1()
# or
#gv_s = step2()
start_step()

With step_end accessing the module level dictionary

def step_end(choice):
    if choice =="A":
        print(gv_s['option_a'])
        gv_s['next_function_a']()
    elif choice =="B":
        print(gv_s['option_b'])
        gv_s['next_function_b']()
    elif choice =="C":
        print(gv_s['option_c'])
        gv_s['next_function_c']()

Instead of a dictionary you could use a class.

wwii
  • 23,232
  • 7
  • 37
  • 77
  • Note that you could in fact modify the globals in the function by calling `globals()` and modifying the returned `dict` in the same way you're doing with `gv_s`. Remove `gv_s` from global scope, and just make the first line of each function `gv_s = globals()` and the rest of the code works as written (and readers of the variables can reference them normally, by name, without `dict` syntax). – ShadowRanger May 30 '19 at 02:26
  • I agree. I stayed away from using `globals()` because most/many/all Q&A's here on SO that mention modifying *that* dict seem to foment disagreement and opinions pro and con. You can edit my answer if you want. Also, I wasn't sure if those *names* existed anywhere outside the functions shown. – wwii May 30 '19 at 02:32
  • It's definitely suspect to do it (major code smell), but it's also definitely legal by the language spec. The part where it gets hairy (as in implementation dependent, and usually incorrect) is modifying the `dict` returned by `locals()` (or equivalently, by `vars()` with no argument inside a function). I'm not going to edit it into the answer, just mentioning it as an option. – ShadowRanger May 30 '19 at 02:41
  • Which option can be considered 'best practice' for keeping the code clean and easy to maintain? – monogr_rigas Jun 18 '19 at 22:23
  • Hard to say without knowing more about your project. I've seen similar questions here on SO and some people like keeping *global* variables (or configuration stuff) in a separate module, import the module wherever needed and access/change with *dot* notation (`module.variable`). I don't like functions with side affects so I might just make two separate dictionaries and assign them to a name when appropriate: `d1={...}; d2={...}; gv_s = d1; #then later; gv_s = d2` – wwii Jun 18 '19 at 22:56
  • so far I reduced the code by 80 lines using your suggestion to 'return a dictionary of options and assign the return value to a name in the module's namespace'. Thank you and everyone else for the replies. Very helpful. Amazingly helpful. Thanks again everyone! – monogr_rigas Jun 19 '19 at 04:38
  • When you decided to use 'gv_s' as the variable name, is there a specific reason you choose to name it this way? It looks like an abbreviation of 'global variables'. Is there group of common abbreviations that I should learn, and if so, is there a page where I can learn them? – monogr_rigas Jun 19 '19 at 17:09
  • I just made it up because it was short and made sense to me. Use names that will mean something to you when you read it two years from now: hopefully they make sense to someone else also. – wwii Jun 19 '19 at 17:28