1

This might seem like a rather weird thing to do, but I was curious if it was possible to implicitly pass a variable down a call chain in Python without passing it as an argument. To better illustrate here is an example:

Here is the "normal" way:

def three(something):
    print(something)

def two(something):
    # ...
    three(something)

def one(something):
    # ...
    two(something)

And here is what I want to be able to do:

def three():
    # something is defined implicitly
    print(something)

def two():
    # ...
    three()

def one(something):
    # somehow define something inside a context
    # for this activation 
    two()

For the purpose of this, one, two and three are not in the same class or even the same module.

Kristina
  • 15,859
  • 29
  • 111
  • 181
  • If they are not in the same class or module, what's the guarantee there will be a `something` defined when `three` is called? – StoryTeller - Unslander Monica Feb 22 '13 at 20:45
  • Nothing. This is a very terrible hack, but I still want it to work. – Kristina Feb 22 '13 at 20:47
  • No you don't. You may be interested in it for curiosity or something, but you don't want to actually write code like this ;-) FWIW I think it's impossible without changing the language rather deeply (specifically the scoping rules which statically determine which names are looked up in which scope). –  Feb 22 '13 at 20:50
  • 2
    See [Get locals from calling namespace in Python](http://stackoverflow.com/q/6618795/222914) – Janne Karila Feb 22 '13 at 20:58
  • @Janne Karila Submit that as an answer. You deserve the credit for the reference. – Mel Nicholson Feb 22 '13 at 21:01
  • @Janne Karila That requires explicit action on the part of the function that wants to use the variable. – Kristina Feb 22 '13 at 21:03
  • What's the use case or the problem you are trying to solve? – Burhan Khalid Feb 22 '13 at 21:06
  • @Burhan Khalid There isn't one. This is a purely hypothetical question. – Kristina Feb 22 '13 at 21:07
  • 2
    So you are looking for a solution for a problem that doesn't exist? If you provide some context perhaps that might help. – Burhan Khalid Feb 22 '13 at 21:09
  • 1
    Are you concerned with the case of threading? If not, then the call stack should never be interrupted - so how do globals not suffice? If threading is a concern, then why not key in globals? At some level there will have to be explicit action to support what you are asking for - are you looking for language features or is some sort of framework/use of magic language features going to suffice for your answer? – Jeremy Brown Feb 22 '13 at 21:21

4 Answers4

2
  1. You don't want to do this.

  2. If you are really convinced that you want to torture yourself, then you could create a separate thread and run the call to one() in that thread. Then just use threading.local for the shared state.

  3. You really don't want to do this.

Here's how you can use thread local storage:

import threading
state = threading.local()

def three():
    # something is defined implicitly
    print(state.something)

def two():
    # ...
    three()

def one(something):
    # somehow define something inside a context
    # for this activation
    def inner():
        state.something = something
        two()
    t = threading.Thread(target=inner)
    t.start()
    t.join()

if __name__=='__main__':
    one(42)
    one(24)
Duncan
  • 92,073
  • 11
  • 122
  • 156
  • TLS has the overhead of having to create another thread, and is not always acceptable in the situations when you cannot create another thread for one reason or another (ie. event handlers). – Kristina Feb 22 '13 at 21:06
  • Although erm, it would work with `join`. Still has the overhead of creating a thread on the OS level though. – Kristina Feb 22 '13 at 21:12
0

If you must, you can assign values to the function object itself. Ideally, both three() and two() would perform checks that would raise better exceptions than an AttributeError if 'something' is not set correctly.

def three():
    print(three.something)

def two():
    three.something = two.something
    three()

def one(something):
    two.something = something
    two()
Kyle
  • 322
  • 2
  • 7
  • But in this case, two must explicitly do something to pass that thing down to three, which is not what I want. It must be passed down the call chain without any of functions having to do anything. Also, in this scenario, the variables end up being global-ish, instead of being done on per-activation basis. – Kristina Feb 22 '13 at 20:57
  • this would not be re-entrant – Mel Nicholson Feb 22 '13 at 20:57
0

You could take advantage of lexical closures - define two() and three() within the definition of one().

>>> def one(something):
...     def two():
...         three()
...     def three():
...         print something
...     two()
...
>>> one(1)
1
Jeremy Brown
  • 17,880
  • 4
  • 35
  • 28
0

You can use __builtins__ to hold your "global variables".

>>> __builtins__.something = "Hello world!"
>>> def foo():
    print something


>>> foo()
Hello world!

This can help you avoid passing variables explicitly.
This is a terrible hack. IMHO, you really don't need to do that.

nymk
  • 3,323
  • 3
  • 34
  • 36