Summary
A discussion of a simple method in Python to implement the equivalent of a c programming style switch.
Story
Recently, I was working on an example where I would have used something like this if I was programming in C (emphasis on like)
#include <stdio.h> typedef enum { op0, op1, op2, op3 } opcodes_t; int main(int argc,char **argv) { opcodes_t excode = op1; switch(excode) { case 0: printf("opcode 0"); break; case 1: printf("opcode 1"); break; case 2: printf("opcode 2"); break; case 3: printf("opcode 3"); break; } }
And, as you know, I am not a real Python programmer. I tried switch… obviously to no avail. So now what? Well in the example above I have
- A list of keys, the enumerated opcode_t
- That have a values e.g. “opcode 0” etc.
Sounds like a dictionary. Here is the equivalent code in Python:
opcode_t = { 0:"opcode 0", 1:"opcode 1", 2:"opcode 2", 3:"opcode 3", } ex1 = 1 print(opcode_t[ex1])
But what happens if a key is missing? The bracket[] method of a dictionary is equivalent to the dictionary method “get”. The “get” method has a default case if they key is missing from the dictionary. Here is the example code:
opcode_t = { 0:"opcode 0", 1:"opcode 1", 2:"opcode 2", 3:"opcode 3", } ex1 = 1 # will return "not found" if the key is not in the dictionary print(opcode_t.get(ex1,"Not Found"))
What if you want to do something? Values in dictionaries can be “function pointers” (is what I would call them in C). They are probably properly called references in Python. Regardless, here is some absurd code that demonstrates the example.
def opcode0(): return "opcode 0" def opcode1(): return "opcode 1" def opcode2(): return "opcode 2" def opcode3(): return "opcode 3" opcode1_t = { 0:opcode0, 1:opcode1, 2:opcode2, 3:opcode3, } ex1 = 1 print(opcode1_t[ex1]())
My mDNS Example
The example that lead me to this problem was decoding mDNS headers which have 8 fields of different lengths and possible values. Here is what I actually did:
qrText = {0:"Query",1:"Response"} opcodeText = {0:"Query",1:"IQuery",2:"Status",3:"reserved",4:"Notify",5:"Update"} aaText = {0:"Non Authoratative",1:"Authoratative"} tcText = {0:"No Truncation",1:"Truncation"} rdText = {0:"No Recursion",1:"Recursion"} raText = {0:"No Recursion Available",1:"Recursion Available"} zText = {0:"Reserved"} rcodeText = { 0:"No Error", 1:"Format Error", 2:"Server Failure", 3:"Name Error", 4:"Not implemented", 5:"Refused - probably a policy reason", 6:"A name exists when it should not", 7:"a resource record set exists that should not", 8:"NX RR Set - A resource record set that should exist does not", 9:"Not Authorized", 10:"Not Zone", } def printHeader(self): print(f"id = {self.id}") print(f"qr = {self.qr} {self.qrText.get(self.qr,'Unknown')}") print(f"opcode = {self.opcode} {self.opcodeText.get(self.opcode,'Unknown')}") print(f"aa = {self.aa} {self.aaText.get(self.aa,'Unknown')}") print(f"tc = {self.tc} {self.tcText.get(self.tc,'Unknown')}") print(f"rd = {self.rd} {self.rdText.get(self.rd,'Unknown')}") print(f"ra = {self.ra} {self.raText.get(self.ra,'Unknown')}") print(f"z = {self.z} {self.zText.get(self.z,'Unknown')}") print(f"response code = {self.rcode} {self.rcodeText.get(self.rcode,'Unknown')}") print(f"rc question count = {self.qdcount}") print(f"answer count = {self.ancount}") print(f"name server count = {self.nscount}") print(f"additional record count = {self.arcount}")
No comment yet, add your voice below!