[QUOTE=Theodox;21691]well, part of it is Iâd just let the command fail on bad inputs - thatâs how you find the bugs :). flat If-tests , like âif not node: returnâ are fine too: they let you bail out as needed and donât complicate program flow. Whatâs bad is multiple tests of the same value, or deeply nested tests, or worst of all both at once â it can quickly become impossible to be sure what code is running. If youâve never read it, The Art Of Readable Code has a great discussion of if-ing
However the pythonic alternative for multiple choice situations is to use dictionary lookups
def foo (node):
nt = cmds.nodeType(mesh) # let it fail - if i call it empty, the bug needs to be identified
def mesh_func(node):
print "I'm a mesh"
def dummy_func(node):
print "i dont know what to do with %s" % node
curve_func = lambda p: print p + "is a curve"
opts = {'mesh': mesh_func, 'curve': curve_func}
# the callables are stuffed into the dictionary
func_to_call = opts.get(nt , dummy)
# dummy func is the default value
func_to_call(nt )
this looks more complex (for a short example like this it is!) but it has two big plusses:
-
you can expand it easily: the sub-defs or lambdas are clearly self contained. If the same functionality were wrapped in ifs youâd have to watch your indents and exit points all the way down. Adding a new function doesnât disturb existing code at all. And of course they can just be other functions - I only inlined them here to make the point.
-
in practice, you would probably not want the sub defs in line. They could be class member functions (thatâs the most common) but you could also do by configuring at runtime:
def mesh_func (mesh):
#blah
def curve func(curve)(:
#blah
def light_func(light):
# blah
def fail(node):
print "unrecognized node"
def foo (node, **opts)
nt = cmds.nodeType(node)
opts.get(nt, fail)(nt)
def handle_lights_and_curves(node):
foo (node, curve=curve_func, light=light_func)
def handle_curves_and_meshes(node):
foo (node, curve=curve_func, mesh=mesh_func)
[/QUOTE]
âŚit can quickly become impossible to be sure what code is runningâŚ
On that note, when I do have some trouble tracking code flow in loops or branches, even with a IDE and the stack, I fall back to logging.
I recently added this method to my simple logging class I setup, Itâll return nice information about where it was called from - it will log the current function, the file it is in and line number the function starts.
#----------------------------------------------------------------------
def autolog(self, message):
'''Automatically log the current function details.'''
# Get the previous frame in the stack, otherwise it would
# be this function!!!
func = inspect.currentframe().f_back.f_code
# Dump the message + the name of this function to the log.
self.log.debug( '{0}: {1} in {2}:{3}'
''.format( message,
func.co_name,
func.co_filename,
func.co_firstlineno)
) #end
I use it like this:
>> LOG = SetupLogger():
>> logMessage = âTestingâ
>> LOG.autolog(logMessage)
and itâll return something like this>> 2013-06-21 09:33:01,193 - DEBUG - Testing: main in C:\depot\bpTools\maya\python\packages\bp_debugLogger\bp_debugLogger.py:281