27

I have two functions, fun1 and fun2, which take as inputs a string and a number, respectively. They also both get the same variable, a, as input. This is the code:

a = ['A','X','R','N','L']

def fun1(string,vect):
    out = []
    for letter in vect:
        out. append(string+letter)
    return out

def fun2(number,vect):
    out = []
    for letter in vect:
        out.append(str(number)+letter)
    return out

x = fun1('Hello ',a)
y = fun2(2,a)

The functions perform some nonsense operations. My goal would be to rewrite the code in such a way that the variable a is shared between the functions, so that they do not take it as input anymore.

One way to remove variable a as input would be by defining it within the functions themselves, but unfortunately that is not very elegant. What is a possible way to reach my goal?

The functions should operate in the same way, but the input arguments should only be the string and the number (fun1(string), fun2(number)).

2
  • 6
    You are half-way to rediscovering object-oriented programming. Commented Jan 13, 2017 at 14:32
  • 3
    The insides of the function can "see" a. Try just for letter in a: Commented Jan 13, 2017 at 14:33

5 Answers 5

35

Object-oriented programming helps here:

class MyClass(object):
    def __init__(self):
        self.a = ['A','X','R','N','L']  # Shared instance member :D

    def fun1(self, string):
        out = []
        for letter in self.a:
            out.append(string+letter)
        return out

    def fun2(self, number):
        out = []
        for letter in self.a:
            out.append(str(number)+letter)
        return out

a = MyClass()
x = a.fun1('Hello ')
y = a.fun2(2)
Sign up to request clarification or add additional context in comments.

5 Comments

You can remove the vect parameter now. ;)
If a is a constant, it might be better to make it a class attribute (and possibly a tuple). Else, it might be better to pass it to MyClass initializer.
is the operation of defining self.a repeated everytime I call fun1 or fun2? I ask this question because if self.a was obtained after a much more complicated and demanding operation, I would like it to be executed only once and not every time I call fun1 and fun2
Note that this answer, the answer by @Willem Van Onsem, and the answer by @involtus, are all three correct and functional answers, so I've upvoted them all. However, in order of "goodness", the best answer is this object-oriented one here by @ospahiu, with shared member variables, the 2nd best answer is the one by @Willem Van Onsem which recommends what I might call "automatically scoped" read-outer-from-inner variables, and the global answer by @involtus is the least-recommended solution.
@GabrielStaples the latter two answers are really the same thing. What you call "automatically scoped" is still a global - it's just that the global keyword isn't necessary when the value isn't reassigned. While it's generally accepted that reassigning globals is what causes the problems with globals, some problems won't be solved by merely accessing them.
19

An alternative to using classes: You can use the global keyword to use variables that lie outside the function.

a = 5
def func():
    global a
    return a+1

print (func())

This will print 6.

But global variables should be avoided as much as possible.

Comments

8

Since a is defined outside the function scope and before the functions are defined, you do not need to feed it as an argument. You can simply use a.

Python will first look whether the variable is defined in the function scope, and if not, it looks outside that scope.

a = ['A','X','R','N','L']

def fun1(string):
    out = []
    for letter in a:
        out.append(string+letter)
    return out

def fun2(number):
    out = []
    for letter in a:
        out.append(str(number)+letter)
    return out

x = fun1('Hello ')
y = fun2(2)

In this case you can also rewrite your functions into more elegant list comprehensions:

a = ['A','X','R','N','L']

def fun1(string):
    return [string+letter for letter in a]

def fun2(number):
    return [str(number)+letter for letter in a]

x = fun1('Hello ')
y = fun2(2)

5 Comments

Is this actually recommended or should it be avoided?
@Ev.Kounis: it is not considered very elegantly since one might forget that there is a variable defined outside the scope and introduce an a in a function. So one better uses object oriented programming.
@Ev.Kounis : it should definitly be avoided, unless 'a' is a constant (not supposed to change during the whole process life) - and even then it would still be better to explicitely pass a (if you care about testability that is).
I'm not saying this is this answer is the way to go. I was simply trying to point out to the OP that passing a as an argument is useless here. But defining globals is in general not a good idea.
Note that you sometimes don't have a choice. For instance tkinter callbacks often only take one or zero arguments, so you can't pass application state. So the application state has to be in the global scope for callbacks to use it. Note that OOP doesn't really help here. The common pattern of writing a UI class in tkinter often puts the whole program state in that class of which one instance is created. Which basically comes down to the same thing.
0

This can be easily achieved using the global keyword. That makes the a variable available in the whole file. However, the global variables should be avoided as much, because every function has access to these, it becomes increasingly hard to figure out which functions actually read and write these variables.

a = ['A','X','R','N','L']

def fun1(string):
    out = []
    for letter in a:
        out. append(string+letter)
    return out

def fun2(number):
    out = []
    for letter in a:
        out.append(str(number)+letter)
    return out

x = fun1('Hello ')
y = fun2(2,a)

1 Comment

So what is your proposed answer?
0

Object Oriented Programming and making a a member variable is absolutely the best solution here.

But sometimes you have codes that are not OO, think of a flask application for example when you have multiple endpoints that you'd like to share some value. In this case, a shared or global variable is the way to go. Meaning defining the varibale outside the scope of all the methods so it could be accessed anywhere.

Now, if the value of your variable never changes, you sould use uppercase letters for naming, to mark it as final and in a sense global (similar to final static variables in Java).

A = ['A','X','R','N','L']

But if the value does change, first, the name should be lowercase a = ['A','X','R','N','L']. Second, you'd want to limit the places where the value can change, ideally to only one method, and there you can use the global keyword to change the value

a = ['A','X','R','N','L']

def fun1(string,vect):
    global a
    a.append('W')

    out = []
    for letter in vect:
        out. append(string+letter)
    return out

def fun2(number,vect):
    out = []
    for letter in vect:
        out.append(str(number)+letter)
    return out

x = fun1('Hello ',a)
y = fun2(2,a)

If you find yourself changing the value of a in multiple places in your code, a shared/global variable is probably not what you're looking for and instead you should just pass the value around as a parameter.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.