Firguring out a Case/Switch statement

I’ve been working on some code that includes a “switch” statement for calling into functions (as a result of GUI button presses). When pressing a button, I keep getting:

“Function is Traceback (most recent call last):
File “D:_Technical_Tech_Projects\Freq_Cntr_LC_and_ESR_Meter_Software_System Software\FreqCntr_Sys.pyw”, line 437, in FrontPanelBtns_Proc
self.fpfunc_slct(buttonslct) # Go do function
File “D:_Technical_Tech_Projects\Freq_Cntr_LC_and_ESR_Meter_Software_System Software\FreqCntr_Sys.pyw”, line 467, in fpfunc_slct
return func()
TypeError: ‘NoneType’ object is not callable” error and it’s unclear why that is. Here’s that piece of my code:

(lots of snipping)...		  
       self.fpfunc_slct(buttonslct)		(Line 437)								# Go do function
		#
	def fpfunc_slct(self, buttonslct):
		global pwrbtn_id, cplbtn_id, attnbtn_id, impbtn_id, lpfbtn_id, slpstartbtn_id, slpstopbtn_id
		global enterbtn_id, uparrbtn_id, dwnarrbtn_id, lftarrbtn_id, rtarrbtn_id, PWR_ON
		if PWR_ON == False:												# If power is off, allow only ON/STANDBY button. Else allow all
			buttonslct = self.pwr_btn
		#	buttonslct.Id = self.pwr_btn.id
		#
		btnslct_id = buttonslct.Id
		switcher={
			pwrbtn_id:self.pwrbtn_func(buttonslct),						# ON/OFF function
			cplbtn_id:self.cplngbtn_func(buttonslct),					# Set AC/DC input
			attnbtn_id:self.atten_func(buttonslct),						# x1/X10 input select
			impbtn_id:self.impedance_func(buttonslct),					# Set 50/1M ohms input
			lpfbtn_id:self.lpf_func(buttonslct),						# En/dis-able LPF
			slpstartbtn_id:self.slopestart_func(buttonslct),			# Set start slope
			slpstopbtn_id:self.slopestop_func(buttonslct),				# Set end slope
			enterbtn_id:self.mainmenu_func(buttonslct),					# Enter menu select
			uparrbtn_id:self.uparrow_func(buttonslct),					# Up/incr
			dwnarrbtn_id:self.downarrow_func(buttonslct),				# Down/decr
			lftarrbtn_id:self.leftarrow_func(buttonslct),				# Left/prev select
			rtarrbtn_id:self.rightarrow_func(buttonslct)				# Right/next select
				}
		func=switcher.get(btnslct_id, lambda :"Invalid")
		return func()                      (line 467)

I did a print of “func” and it certainly shows nothing has been registered. I have also dumped buttonslct (name, Id, etc.) and it appears the calling routine is passing correct info.

The self.fpfunc_slct(buttonslct), line 437, is at the end of a routine that captures the button event and passes the button object in buttonslct (D’oh! :slight_smile:

Also, the “if PWR_ON == False:” statement is used to disable all other GUI buttons (except for the ON/STANDBY button) if power is off.

One of the things I’m unsure abt is if I can pass “buttonslct” again, or at all, thru my switch/dictionary to the function that actually processes the specific button.

Thanks in advance for any insight and help on this…cheers…

Do the foo_func functions return the function you want to call? Or are they the actual function you want to call? I suspect it’s the latter. When you do this:

        switcher={
			pwrbtn_id:self.pwrbtn_func(buttonslct),
        ...

Then Python is calling self.pwrbtn_func and putting the return value, None, into the dictionary. You probably meant to do something like this:

        switcher={
			pwrbtn_id:self.pwrbtn_func,
        ...

and

        return func(buttonsslct)

That will put the self.pwrbtn_func method itself into the dictionary.

Robin…

You’re correct. They are the functions I want to call. My guess is that what’s in the “dictionary/switcher” is an object reference(?) that’s passed to the “return funct(buttonslct)” statement. As such, no value (“buttonslct”…which is what I want to pass to the function) is not being seen so that’s why “return func(buttonslct)” is trying to make an empty method/function call…if I got that right (I come from assembly level programming where I can get direct access to the h/w like code and data memory space. Working in this more “abstract” coding environment is taking some getting used to :frowning: I’m working on it, tho…

Your suggestion not only solved my problem, but also simplified the code. It was how this code section started out but then trying to figure out how to pass the value “buttonslct” sort of took me to the “dark side” :wink:

Thanks much for the help and support…cheers…

The key takeaway is that “functions are objects too” and, as such, can be passed around like any other object reference. In this case you put the functions (actually “bound methods” to be totally accurate) as values into the dictionary. Then you fetched one of these function objects from the dictionary and called it by using the (...).

Robin,

Again, my appreciation. I have a couple of followup questions (had a 3rd but forgot what that was and should prob’ly be in a new thread anyway :slight_smile: As I mentioned, I’m used to coding in assembler which is very directly related to the h/w and am looking for ways to better understand Python, specifically, and high-level languages, in general.

My app emulates a piece of “test” equipment and am using wxPython to create a front panel of (mostly) buttons/toggle buttons and statictext (large results display and smaller informational displays) GUI objects.

  1. bound methods - Since I need to check status of the buttons and modify/update the statictext objects, I assumed (maybe mistakenly) I needed bound methods so I could access the GUI objects. Is it possible (and does it even make sense) to use unbound methods to do this? If so, how do I get access to the GUI objects? Or, do I just end up creating bound methods anyways? If not, I suppose I could create unbound methods/functions that process/update variables (but not GUI objects) based on button values (passed to it) and the modified variables are returned so the bound method can modify the GUI object.

  2. The switch case method - if I understand you correctly, by trying to include the parameter I want to pass in the dictionary statement Python doesn’t process it the way I thought it would be. So, could I do my original dictionary statement,

pwrbtn_id:self.pwrbtn_func(buttonslct),

and not worry about any return? Or is it the way this works in that the call to the switcher method

self.fpfunc_slct(buttonslct)

is where the real action takes place and the switcher method does nothing more than setting up this call by retrieving the method object (?) from the dictionary, and that’s why the parameter(s) I want to pass have to be added in at the end?

Is there a way to create this by using a tuple in the dictionary, i.e.

pwrbtn_id:(self.pwrbtn_func, buttonslct),

but then, I suppose I would have to “decode” and restructure the tuple anyway so I was able to use the method call and parameter I want to pass.

Thanks again and cheers…Steph

  1. It really depends on how you design and organize your application. In the case of event handlers wxPython doesn’t care what is bound as an event handler as long as it is callable and accepts and event object parameter. But if you want to organize your application using object oriented principles then you’ll want to encapsulate the data attributes and the functions that operate upon them into a class, or classes, and so you’ll likely be using bound methods. The same applies in other aspects of your application too.

  2. When you have something like obj.methodName in your code then that expression evaluates to a bound method object, which is actually another way to say that it’s a callable object that automatically passes obj as the first parameter when it is called. (obj might be self, or it might be any other object reference.) If you add the parenthesis to it, like obj.methodName(arg1, arg2) then it first evaluates to the bound method object just like before, and then it calls that callable object. When all is said and done, it ends up being more or less equivalent to this:

    obj.__class__.methodName(obj, arg1, arg2)
    

    In your case you wanted to put callable objects into a dictionary as values, so you could use the dictionary mapping as a way to quickly select the object to be called later. But when you used pwrbtn_id:self.pwrbtn_func(buttonslct), then what happens is that it fully evaluates the bound method, calls it, and then puts the return value of pwrbtn_func into the dictionary as the value part of the key value pair. So that is not what you wanted. Instead, if you put just the pwrbtn_id:self.pwrbtn_func key:value pair into the dictionary then instead of calling self.pwrbtn_func it just uses it as the value, which you can then fetch from the dictionary later and call.

    There are ways to associate parameters with the callable you put into the dictionary such that they will be automatically passed when calling the callable, but I suspect that pointing you in that direction now would just confuse things even more for you.

    Instead, you could do like you suggested and put the callable and the args into the dictionary as a tuple. Or you could use instances of some other class that encapsulates the callable, the args to pass to it, and a method to be used to perform the call passing the saved parameters. Or you can just use a big if btnslct_id== ... elif ... elif ... statement and not worry about using the switcher dictionary at all. The point is that Python is flexible enough that you can use whatever makes sense to you, and in most cases will work fine and be fast enough no matter what you do. More advanced techniques will come with time as you come to understand more and more about the language, and optimizations can be done later in refactoring passes when needed.