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.
You could use Double Dispatch or Multimethods.
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.
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.
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.
精彩评论