I have a function where I need to generate different output strings for another program I invoke, depending on which type it wants.
Basically, the called program needs a command line argument telling it which type it was called with.
Happily I found this answer on SO on how to check a variable for type. But I noticed how people also raised objections, that checking for types betrays a "not object oriented" design. So, is there some other way, presumable more "more object oriented" way of handling this without explicitly checking for type?
The code I have now goes something like this:
def myfunc(val):
cmd_type = 'i'
if instance(val, str):
cmd_type = 's'
cmdline = 'magicprogram ' + cmd_type + ' ' + val
Popen(cmdline, ... blah blah)
...
which works just fine, but I just wanted to know if there is some technique I am unaware of.
I don't think Double Dispatching or Multimethods are particularly relevant nor have much to do with the objections people had to that other SO answer.
Not surprisingly, to make what you're doing more object-oriented, you'd need introduce some objects (and corresponding classes) into it. Making each value an instance of a class would allow -- in fact, virtually force -- you to stop checking its type. The modifications to your sample code below show a very simple way this could have been done:
class Value(object):
""" Generic container of values. """
def __init__(self, type_, val):
self.type = type_ # using 'type_' to avoid hiding built-in
self.val = val
def myfunc(val):
# Look ma, no type-checking!
cmdline = 'magicprogram {obj.type} {obj.val}'.format(obj=val)
print 'Popen({!r}, ... blah blah)'.format(cmdline)
# ...
val1 = Value('i', 42)
val2 = Value('s', 'foobar')
myfunc(val1) # Popen('magicprogram i 42', ... blah blah)
myfunc(val2) # Popen('magicprogram s foobar', ... blah blah)
It would be even more object-oriented if there were methods in the Value
class to access its attributes indirectly, but just doing the above gets rid of the infamous type-checking. A more object-oriented design would probably have a different subclass for each kind of Value
which all share a common set of methods for clients, like myfunc()
, to use to create, manipulate, and extract information from them.
Another benefit of using objects is that you shouldn't have to modify myfunc()
if/when you add support for a new type of 'Value` to your application -- if your abstraction of the essence of a "Value" is a good one, that is.