Python Forum
same context functions - Printable Version

+- Python Forum (https://python-forum.io)
+-- Forum: General (https://python-forum.io/forum-1.html)
+--- Forum: News and Discussions (https://python-forum.io/forum-31.html)
+--- Thread: same context functions (/thread-19168.html)

Pages: 1 2


same context functions - Skaperen - Jun-16-2019

is there a way to define or call a function such that it runs (or is defined to be in) the same context as the caller (e.g. uses the exact same local space and global space as the caller).

i do understand that it can be argued that this would be "side effect hell" because such a function could change anything. my need involves a chunk of code that is repeated many time in the same function. this chunk of code does assign to about a dozen different variables. it would be nice to encapsulate it somewhere so it can simply be called with 1 line instead of having 20 lines of code everywhere it is needed.


RE: same context functions - Yoriz - Jun-16-2019

A class or a module maybe, hard to tell without seeing example code.


RE: same context functions - DeaD_EyE - Jun-16-2019

You can use contextvars for it:

import asyncio
from contextvars import ContextVar
from contextvars import Context

async def foo():
    # code...
    # result ...
    ctx.set(42)
    

async def main():
    await foo()
    return ctx.get()

print('Async code')
ctx = ContextVar('first_ctx')
ctx.set('Foo')
print('Context before running main:', ctx.get())
print('Context from main:', asyncio.run(main()))
print('Context after running main:', ctx.get())
print()


print('Sync code')
context = Context()
ctx2 = ContextVar('second_ctx')
ctx2.set(1337)

def foo2():
    # code ...
    # result ...
    ctx2.set(13)


def main2():
    foo2()
    return ctx2.get()

print('Context before running main2:', ctx2.get())
print('Context from main2:', context.run(main2))
print('Context after running main2:', ctx2.get())
Async code
Context before running main: Foo
Context from main: 42
Context after running main: Foo

Sync code
Context before running main2: 1337
Context from main2: 13
Context after running main2: 1337
In async code you don't have to use the context explicit to run functions.
This module was made for asyncio, but it's also usable in synchronous code.


RE: same context functions - Skaperen - Jun-16-2019

is contextvars something new since 3.5?

(Jun-16-2019, 07:33 AM)Yoriz Wrote: A class or a module maybe, hard to tell without seeing example code.

different ways of doing this could use a wide variety of ways of coding it. i don' know whether you want to see how it could be coded if Python did it the way i envision, or the ugly way i do it now.

def main():

    def dovars(foo) context(main):
        a = config.get('a',None)
        b = config.get('b',None)
        ...
        y = config.get('y',None)
        z = config.get('z',None)
        return

    ...

    if cond1:
        ...
        dovars()
        ...

    if cond2:
        ...
        dovars()
        ....

    ...

    if cond255:
        ...
        dovars()
        ...

    if cond256:
        ...
        dovars()
        ...

    ...

    return
at least there are not 6656 assignment statements.


RE: same context functions - Gribouillis - Jun-16-2019

Skaperen Wrote:my need involves a chunk of code that is repeated many time in the same function.
This violates the DRY principle. Your code can be refactored, to say the least.

Objects were invented to allow several functions to share variables in a common namespace. What you want is
class CookingPot:
    def dovars(self):
        self.a = config.get('a',None)
        self.b = config.get('b',None)
        ...
        self.y = config.get('y',None)
        self.z = config.get('z',None)



RE: same context functions - Skaperen - Jun-17-2019

what refactoring do you suggest?

note that the example i posted is merely an example, not to be focused on as a use case but to illustrate what i am talking about. if there is no way to do what is ask for, i should get "no" from everyone. if i get no solution, maybe my next question is broad enough to cover the ideas you have in mind. but in this instance you have suggested refactoring and that interests me to find out more. but refactoring that is specific to the faux use case i posted as an example won't be a solution to other cases. the only things i can do is post all such code (i really can't do that) or post a description in English text (words).

i can describe another case. it is a loop processing incoming characters where each different character has to be handled in different way, logically, but nearly all of them involve a number of variables in the local context, such as emulating a terminal screen. one way to do this is a series of elif statements testing the characters. a better way (IMHO) would be to have a lookup table for the common control characters (0 to 31 or 0 to 127) in a list that holds a reference to a function to call. but having each call using the same context is the solution to seek. i have ideas, but maybe they are too slow. i am considering the exec() builtin function which means i need to compile dozens of code objects.

yes, i am trying to avoid repeating as much as possible. the example probably overrepeats and it is a bad example. in many cases repeating, or duplicating (depending on context) can't be avoided).

in the C language i could encapsulate a group of code into macro a just repeat the macro where i can't eliminate it entirely. if Python had macros like that, it might be the solution. an example (in C) was a program that manipulated linked lists in many places. calling a linked list library slowed it down substantially. most of the CPU time was spent passing values back and forth. so, i wrote my own set of macros to do linked lists in-context. it was much faster because nothing had to be passed around because of no context switch. i ended up doing so much with macros i even had code that generated macro definitions (to avoid repeating myself). one example was code to generate a macro to multiply matrices of specific dimensions.


RE: same context functions - Skaperen - Jun-17-2019

you know the builtin function locals(). i could pass that to a function instead of passing a whole bunch of variables and getting a whole bunch of them back. i'd just need to code all the accesses in the functions as dictionary accesses.

i wrote a function named caller_locals() that, for the caller of caller_locals(), caller_locals() returns it's caller's locals. so with that, i don't even need to pass locals() to those in-context functions.

from inspect import currentframe
def caller_locals():
    frame = currentframe()
    if not frame:return None
    frame = frame.f_back
    if not frame:return None
    frame = frame.f_back
    if not frame:return None
    return frame.f_locals



RE: same context functions - Gribouillis - Jun-17-2019

Skaperen Wrote:but nearly all of them involve a number of variables in the local context, such as emulating a terminal screen.
Again, the normal pythonic solution is to use instance variables and not variables in the local context. For example
class CookingPot:
    pass

def my_func(lines):
    pot = CookingPot()
    for line in lines:
        for c in line:
            if c == 'a':
                pot.spam = 'foo'
            elif c == 'b':
                pot.spam = 'bar'
            elif c == 'c':
                bacon(pot)

def bacon(pot):
    pot.spam = 'qux'
Also remember that the mapping returned by locals() is not writable.


RE: same context functions - Skaperen - Jun-17-2019

(Jun-17-2019, 06:37 AM)Gribouillis Wrote: Also remember that the mapping returned by locals() is not writable.
it works for me.

and how would you eliminate that long stack of elifs in something like a terminal emulator, where you have a huge selection of different logic to choose from based on an arriving code value?


RE: same context functions - Gribouillis - Jun-17-2019

Skaperen Wrote:how would you eliminate that long stack of elifs
Use hash tables!