Bottom Page

Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
 simplifying a stack of elifs
#1
i have a thread function with a role of handling some commands it gets from a queue. there a lot of statement groups like:
    elif msg[0] == 'some command name':
        ...
        # statements to handle that command
        ...
i was thinking of having a mapping of function references but every command involves accessing and modifying local variables, so the functions aspect of this just makes things worse with all the detail to exchange data with the caller (passing locals() is not an option because modifications can't be done that way). if there was a way to run a function in the same context as the caller, that would be a nice direct solution. even calling exec() with the code in a string because passing locals() is still a dictionary that can't be modified.

any ideas?
What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Quote
#2
Skaperen Wrote:every command involves accessing and modifying local variables
Make these variables members of a class instance and dispatch things on instance methods.
class CommandHandler:
    dispatch = {
        'spam': 'on_spam',
        'eggs': 'on_eggs',
    }

    def __init__(self, ...):
        self.foo = ...
        self.bar = ...

    def __call__(self, msg):
        getattr(self, self.dispatch[msg[0]])()

    def on_spam(self):
        self.foo = 3452

    def on_eggs(self):
        self.foo = 1233
Instance variables are the normal way to share data between several functions.

You can also use new classes to manage individual commands

class Command:
    def __init__(self, handler):
        self.handler = handler

class Spam(Command):
    def run(self):
        self.handler.foo = 1023

class Eggs(Command):
    def run(self):
        self.handler.foo = 1048

class CommandHandler:
    dispatch = {
        'spam': Spam,
        'eggs': Eggs,
    }

    def __init__(self, ...):
        self.foo = ...
        self.bar = ...

    def __call__(self, msg):
        self.dispatch[msg[0]](self).run()
Skaperen likes this post
Quote
#3
i can't use "self" because that might collide with other threads so i think each thread needs its own class or dictionary to keep variables in.

does CPython do locks between threads to access dictionaries? i haven't seen anything to suggest that it does. since Queues are described as having such locks, i suspect nothing else does unless it says it does, so i think i need to keep things separated between threads.

what about defining these action functions inside the function that needs to call them and have it call like this:
    def _agent():
        ...
        def x_foo():
            ... # code to run when request "foo" arrives
        def x_bar():
            ... # code to run when request "bar" arrives

        msg = q.get()
        req = 'x_'+msg[0]
        if req in locals():
            locals()[req]() # call the function to handle this request
What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Quote
#4
Skaperen Wrote:i can't use "self" because that might collide with other threads
Each thread can create its own instance of the class. These instances won't collide.
Skaperen Wrote:what about defining these action functions inside the function that needs to call them
I don't think it helps function to share local variables. It would be a good idea to write a complete working example and compare the different implementations.
Quote
#5
it turns out that variables local to the method are accessible by the inner defined function, so, this is not really an issue, at all. this script shows to me that the inner function can access the variable named "this" (and, even "self").
#!/usr/bin/env python3

class foo:
    def __init__(self):
        return

    def meth(self):
        this = 9
        def inner(arg1):
            print('arg1 =',repr(arg1),flush=1)
            print('this =',repr(this),flush=1)
            print('self =',repr(self),flush=1)
            return
        inner(3)
        return

o = foo()
o.meth()
What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Quote
#6
Accessing a variable for reading is different from updating the variable. You can very well pass locals() around as long as you only read variables.
Quote
#7
i get around that by making the variables be 1-list and update it by indexing [0]. so, i am reading the reference to the list.
What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Quote
#8
So it means that you prefer a list to a class instance to store values, for example mylist[0] = 123 instead of myobj.foo = 123. I tend to prefer the latter because it avoids a magic number and it is extendable: you can add myobj.bar later.

Using inner functions allows you to omit a parameter mylist or myobj or self in the functions. I think it is the only advantage. Again I don't like this trick very much, I prefer plain functions because they are more explicit and more standard.
Quote
#9
no. i actually like the class reference way. i just originally started doing the 1-list way long ago before i knew that classes could be used his way. it's just a design habit i need to break.

i've juggled my design around all this week and have eliminated the inner functions by reducing the number of different message actions to just two, in which the elif stack is now smaller, as the code within each action (what was a function called by name). i might get brave and test this thing this weekend, then code up the backup accelerator that was the original need for it.
What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Quote

Top Page

Possibly Related Threads...
Thread Author Replies Views Last Post
  Simplifying my code ilondire05 5 144 Jul-21-2019, 03:21 AM
Last Post: scidam
  Calculate stack value's GFreenD 7 237 May-12-2019, 09:02 PM
Last Post: Yoriz
  Stack trace shows different exception type than print micseydel 5 543 Apr-01-2019, 10:24 PM
Last Post: micseydel
  How to recognize, what functions are filled in of the Python interpreter stack? AlekseyPython 3 225 Mar-13-2019, 12:14 PM
Last Post: AlekseyPython
  using a list as a stack Skaperen 6 363 Feb-25-2019, 09:25 PM
Last Post: Skaperen
  Current Stack Size ichabod801 1 365 Dec-14-2018, 10:11 PM
Last Post: Larz60+
  My program subtracts fractions, but for some reason isn't simplifying them RedSkeleton007 9 1,423 Mar-03-2018, 11:45 AM
Last Post: Gribouillis
  Stack problem, repeating results that i dont want YapYL 1 686 Dec-10-2017, 04:14 PM
Last Post: hshivaraj
  IndexError: pop from empty stack prateek 0 1,652 Oct-08-2017, 10:22 PM
Last Post: prateek
  Does stack overflow exist in python ? sylas 3 1,100 Jul-23-2017, 09:21 AM
Last Post: sylas

Forum Jump:


Users browsing this thread: 1 Guest(s)