mngeek206
mngeek206

Reputation: 5177

Accept different types in python function?

I have a Python function that does a lot of major work on an XML file.

When using this function, I want two options: either pass it the name of an XML file, or pass it a pre-parsed ElementTree instance.

I'd like the function to be able to determine what it was given in its variable.

Example:

def doLotsOfXmlStuff(xmlData):
    if (xmlData != # if xmlData is not ET instance):
        xmlData = ET.parse(xmlData)
    # do a bunch of stuff
    return stuff

The app calling this function may need to call it just once, or it may need to call it several times. Calling it several times and parsing the XML each time is hugely inefficient and unnecessary. Creating a whole class just to wrap this one function seems a bit overkill and would end up requiring some code refactoring. For example:

ourResults = doLotsOfXmlStuff(myObject)

would have to become:

xmlObject = XMLProcessingObjectThatHasOneFunction("data.xml")
ourResult = xmlObject.doLotsOfXmlStuff()

And if I had to run this on lots of small files, a class would be created each time, which seems inefficient.

Is there a simple way to simply detect the type of the variable coming in? I know a lot of Pythoners will say "you shouldn't have to check" but here's one good instance where you would.

In other strong-typed languages I could do this with method overloading, but that's obviously not the Pythonic way of things...

Upvotes: 1

Views: 775

Answers (5)

Jim Dennis
Jim Dennis

Reputation: 17520

The principle of "duck typing" is that you shouldn't care so much about the specific type of an object but rather you should check whether is supports the APIs in which you're interested.

In other words if the object passed to your function through the xmlData argument contains some method or attribute which is indicative of an ElementTree that's been parsed then you just use those methods or attributes ... if it doesn't have the necessary attribute then you are free to then pass it through some parsing.

So functions/methods/attributes of the result ET are you looking to use? You can use hasattr() to check for that. Alternatively you can wrap your call to any such functionality with a try: ... except AttributeError: block.

Personally I think if not hasattr(...): is a bit cleaner. (If it doesn't have the attribut I want, then rebind the name to something which has been prepared, parsed, whatever as I need it).

This approach has advantages over isinstance() because it allows users of your functionality to pass references to objects in their own classes which have extended ET through composition rather than inheritance. In other words if I wrap an ET like object in my own class, and expose the necessary functionality then I should be able to pass reference s to your function and have you just treat my object as if it were a "duck" even if it wasn't a descendant of a duck. If you need feathers, a bill, and webbed feet then just check for one of those and try to use the rest. I may be a black box containing a duck and I may have provided holes through which the feet, duck-bill, and feathers are accessible.

Upvotes: 2

ah008a
ah008a

Reputation: 1145

I think you can just compare the data types:

if (xmlData.dtype==something):
    call Function1
else:
    call Function2

Upvotes: 0

user856358
user856358

Reputation: 593

Can you try to put an if statement to check the type and determine what to run from there?

if type(xmlData).__name__=='ElementTree':
    #do stuff
else: 
    #do some other stuff

Upvotes: 0

ecatmur
ecatmur

Reputation: 157484

This is a fairly normal pattern (e.g. Python function that accepts file object or path). Just use isinstance:

def doLotsOfXmlStuff(xmlData):
    if not isinstance(xmlData, ET):
        xmlData = ET.parse(xmlData)
    ...

If you need to do cleanup (e.g. closing files) then calling your function recursively is OK:

def doLotsOfXmlStuff(xmlData):
    if not isinstance(xmlData, ET):
        xmlData = ET.parse(xmlData)
        ret = doLotsOfXmlStuff(xmlData)
        ... # cleanup (or use a context manager)
        return ret
    ...

Upvotes: 2

Ayman
Ayman

Reputation: 11585

You can use isinstance to determine type of variable.

Upvotes: 0

Related Questions