Fibo Kowalsky
Fibo Kowalsky

Reputation: 1248

Access variable declared in a function from another function

How to access a variable x that is local for a function main() from another function sub() that is called inside main()?

The MEW below throws an error NameError: global name 'x' is not defined when the script is run.

If I add global x, l in main() there are no errors anymore, but I do not want to make these variables global as such.

MWE:

def sub():
    print "x from main: {}".format(x)
    print "l from main: {}".format(l)


def main():
    x = 5
    l = [1,2,3,4,5]
    print "z from top: {}".format(z)
    sub()


if __name__ == "__main__":
    z = 'SOME'
    main()

Edit1 I am editing an existing code and prefer not to change arity of functions

Upvotes: 1

Views: 75

Answers (2)

NL23codes
NL23codes

Reputation: 1209

The best way to do what you'd like, also requires the least amount of changes. Just change the way you're declaring the variables; by adding the function name before the variable name, you're able to access the variable from anywhere in your code.

def sub():
    print "x from main: {}".format(main.x)
    print "l from main: {}".format(main.l)

def main():
    main.x = 5
    main.l = [1,2,3,4,5]
    print "z from top: {}".format(z)
    sub()

if __name__ == "__main__":
    z = 'SOME'
    main()

This will print:

z from top: SOME
x from main: 5
l from main: [1, 2, 3, 4, 5]

Upvotes: 1

Jab
Jab

Reputation: 27485

Passing the parameters as arguments gets what you want done.

def sub(x, l):
    print "x from main: {}".format(x)
    print "l from main: {}".format(l)


def main(z):
    x = 5
    l = [1,2,3,4,5]
    print "z from top: {}".format(z)
    sub(x, l)


if __name__ == "__main__":
    z = 'SOME'
    main(z)

This prints:

z from top: SOME
x from main: 5
l from main: [1, 2, 3, 4, 5]

If you don't want to use this approach, I guess you could follow this answer's approach, although seems convoluted for a case like this.

import inspect

z = 'SOME'

def sub():
    frame = inspect.currentframe()
    locales = None
    try: locales = frame.f_back.f_locals
    finally: del frame
    if locales:
        for k, v in locales.items():
            print("{} from main: {}".format(k, v))


def main():
    x = 5
    l = [1,2,3,4,5]
    print("z from top: {}".format(z))
    sub()

main(z)

Maybe this would be cleaner if you could turn this into a context manager.

You could also use *args and *kwargs

def function(*args, **kwargs):
    if args:
        for a in args:
            print "value: {} was passed in args".format(a)
    if kwargs:
        for k, v in kwargs.items():
            print "key {} was passed with value: {} in kwargs".format(k, v)


def caller():
    x = 5
    l = [1,2,3,4,5]
    function(x, l = l)

caller()
#value: 5 was passed in args
#key l was passed with value: [1, 2, 3, 4, 5] in kwargs

From here you can assign values to locals

within function

locals['x'] = args[0]
locals['l'] = kwargs[l]

OR use dict.get this way you can set defaults:

l = kwargs.get('l', None)

Upvotes: 3

Related Questions