Python Forum
simplifying a stack of elifs
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?
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply
#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()
Reply
#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
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply
#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.
Reply
#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()
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply
#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.
Reply
#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.
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply
#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.
Reply
#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.
Tradition is peer pressure from dead people

What do you call someone who speaks three languages? Trilingual. Two languages? Bilingual. One language? American.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  Help for simplifying code mmk1995 8 4,087 Sep-24-2019, 02:04 PM
Last Post: perfringo
  a stack of elifs and a try/except Skaperen 2 1,938 Sep-12-2019, 11:39 PM
Last Post: Skaperen
  Simplifying my code ilondire05 5 3,687 Jul-21-2019, 03:21 AM
Last Post: scidam
  My program subtracts fractions, but for some reason isn't simplifying them RedSkeleton007 9 5,742 Mar-03-2018, 11:45 AM
Last Post: Gribouillis
  Simplifying multiple "or" conditions in if statement. rhubarbpieguy 8 101,892 Jul-22-2017, 12:19 PM
Last Post: rhubarbpieguy

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020