Search code examples
pythondesign-patternscoding-style

Elegant pattern for mutually exclusive keyword args?


Sometimes in my code I have a function which can take an argument in one of two ways. Something like:

def func(objname=None, objtype=None):
    if objname is not None and objtype is not None:
        raise ValueError("only 1 of the ways at a time")
    if objname is not None:
        obj = getObjByName(objname)
    elif objtype is not None:
        obj = getObjByType(objtype)
    else:
        raise ValueError("not given any of the ways")

    doStuffWithObj(obj)

Is there any more elegant way to do this? What if the arg could come in one of three ways? If the types are distinct I could do:

def func(objnameOrType):
    if type(objnameOrType) is str:
        getObjByName(objnameOrType)
    elif type(objnameOrType) is type:
        getObjByType(objnameOrType)
    else:
        raise ValueError("unk arg type: %s" % type(objnameOrType))

But what if they are not? This alternative seems silly:

def func(objnameOrType, isName=True):
    if isName:
        getObjByName(objnameOrType)
    else:
        getObjByType(objnameOrType)

cause then you have to call it like func(mytype, isName=False) which is weird.


Solution

  • How about using something like a command dispatch pattern:

    def funct(objnameOrType):
       dispatcher = {str: getObjByName,
                     type1: getObjByType1,
                     type2: getObjByType2}
       t = type(objnameOrType)
       obj = dispatcher[t](objnameOrType)
       doStuffWithObj(obj)
    

    where type1,type2, etc are actual python types (e.g. int, float, etc).