Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the canonical way of handling different types in Python?

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.

like image 354
Prof. Falken Avatar asked Oct 29 '10 12:10

Prof. Falken


4 Answers

You could use Double Dispatch or Multimethods.

like image 128
pillmuncher Avatar answered Sep 28 '22 08:09

pillmuncher


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.

like image 44
martineau Avatar answered Sep 28 '22 08:09

martineau


    But I noticed how people also raised objections, 
that checking for types betrays a "not object oriented" design

Actually it's called Duck typing style ("If it looks like a duck and quacks like a duck, it must be a duck."), and it's the python language that recommend using this style of programming .

and with duck typing come something call EAFP (Easier to Ask Forgiveness than Permission)

    presumable more "more object oriented" way of handling this without 
   explicitly checking for type?

you mean more pythonic, basically what will be more pythonic in your case is something like this:

def myfunc(val):
    cmd_type = 'i'

    # forget about passing type to your magicprogram
    cmdline = 'magicprogram  %s ' % val 
    Popen(cmdline, ... blah blah)

and in your magicprogram (i don't know if it's your script or ...), and because in all cases your program will get a string so just try to convert it to whatever your script accept;

from optparse import OptionParser

# ....

if __name__ == '__main__':

    parser = OptionParser(usage="blah blah")

    # ...
    (options, args) = parser.parse_args()

    # Here you apply the EAFP with all type accepted.
    try:
        # call the function that will deal with if arg is string
        # remember duck typing.
    except ... :
        # You can continue here

I don't know what's all your code, but you can follow the example above it's more pythonic, and remember every rule has their exception so maybe your case is an exception and you will better be with type checking.

Hope this will clear things for you.

like image 21
mouad Avatar answered Sep 28 '22 09:09

mouad


This is more of an engineering in the large question than how to design one small function. There are many different ways to go about it, but they more or less break down to the same general thought process. Back where the type of val is known it should specify how it should be translated into a command line arg. If it were me I would probably make val be a class that had a To Command Line function that did the right thing. You could also assign a type specific myfunc function to a variable then call that when you need to.

edit: To explain the last version something along the lines of

Val = "a string"
myfunc = myfuncStringVersion

more or less doing the same thing you would with wrapping val in a class only broken out into a value and function since you might not want to wrap val in a class.

like image 40
stonemetal Avatar answered Sep 28 '22 08:09

stonemetal