4

Are the functions able to have double names for their arguments in Python? I mean a short and full form of the variable name.

I'll try to be more clear. Everybody who is familiar with Autodesk Maya knows a function to make constraints. It has some flags and you can use the short or the long form of it's name:

maintainOffset(mo), weight(w), layer(l) and so on..

So you can call this function with different names of it's arguments but it gives you the same result:

cmds.parentConstraint(driverObj, drivenObj, maintainOffset=True, weight=1.0,..)
cmds.parentConstraint(driverObj, drivenObj, maintainOffset=True, w=1.0,..)
cmds.parentConstraint(driverObj, drivenObj, mo=True, weight=1.0,..)
cmds.parentConstraint(driverObj, drivenObj, mo=True, w=True,..)

How to implement this type of behaviour in Python 2.7.x? I'm using the documentation actively but still can't find the answer.

Besides I have defined 4 functions for various types of the contraints:

# Parent Constraint
def doParentConstraint(lst, mo = True, w = 1.0, sr = 'None', st = 'None', l = 'None'):
    for pair in lst:
        cmds.parentConstraint(pair[0], pair[1], maintainOffset = mo, weight = w,
                                skipRotate = sr, skipTranslate = st, layer = l)
# Orient Constraint
def doOrientConstraint(lst, mo = False, w = 1.0, o = (0.0, 0.0, 0.0), sk = 'None', l = 'None'):
    for pair in lst:
        cmds.orientConstraint(pair[0], pair[1], maintainOffset = mo, weight = w, 
                                offset = o, skip = sk, layer = l)
# Point Constraint
def doPointConstraint(lst, mo = False, w = 1.0, o = (0.0, 0.0, 0.0), sk = 'None', l = 'None'):
    for pair in lst:
        cmds.orientConstraint(pair[0], pair[1], maintainOffset = mo, weight = w, 
                                offset = o, skip = sk, layer = l)
# Scale Constraint
def doScaleConstraint(lst, mo = False, w = 1.0, o = (0.0, 0.0, 0.0), sk = 'None', l = 'None'):
    for pair in lst:
        cmds.orientConstraint(pair[0], pair[1], maintainOffset = mo, weight = w, 
                                offset = o, skip = sk, layer = l)

Connection List:

cLst = produceConnectionList(tNodesA, tNodesB)

And some sort of wrapper function for all of these:

def batchConnect(type = "parentConstraint", ???):
    cLst = produceConnectionList(tNodesA, tNodesB)
    if type is "parentConstraint": doParentConstraint(cLst, ???)
    if type is "orientConstraint": doOrientConstraint(cLst, ???)
    if type is "pointConstraint": doPointConstraint(cLst, ???)
    if type is "scaleConstraint": doScaleConstraint(cLst, ???)

But I don't know how to pass values between these functions and the wrapper because although they have the same number of arguments but the names of arguments and those types a little bit differ.

Also I want to have an opportunity to use short and the full form of the flag names when I'm calling the wrapper:

batchConnect("pointConstraint", mo=False, offset=(0,0,0), weight=1)
batchConnect("pointConstraint", maintainOffset=False, o=(0,0,0), w=1)

Must do the same.

batchConnect("pointConstraint")

Without arguments must call doPointConstraint() with default values.

batchConnect()

Without specifying the type and arguments at all must call by default doParentConstraint() with these default values

Zoe
  • 27,060
  • 21
  • 118
  • 148
Michael
  • 340
  • 3
  • 13

2 Answers2

5

If I've correctly understood what you're after, the easiest way to do this is with **kwargs "magic" and dict.get, for example:

def demo(maintain_offset=None, **config):
    if maintain_offset is None:  # if not supplied by 'real name'
        maintain_offset = config.get('mo', 'baz')  # check alias, fall back to default
    print maintain_offset

(note compliance with the style guide). In use:

>>> demo(maintain_offset="foo")
foo
>>> demo(mo="bar")
bar
>>> demo()
baz

If this syntax is unfamiliar, see What does ** (double star) and * (star) do for parameters?


For a more generic approach, you can use the decorator syntax to wrap a function with a set of aliases:

import functools

def alias(aliases):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(**kwargs):
            for name, alias in aliases.items():
                if name not in kwargs and alias in kwargs:
                    kwargs[name] = kwargs[alias]
            return func(**kwargs)
        return wrapper
    return decorator

In use:

>>> @alias({'maintain_offset': 'mo'})
def demo(maintain_offset='baz', **kwargs):
    print maintain_offset


>>> demo(maintain_offset="foo")
foo
>>> demo(mo="bar")
bar
>>> demo()
baz
Community
  • 1
  • 1
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • I don't really ever do decorators, but should `wrapper` be `wrapper(*args, **kwargs)` and the return statement be `return func(*args, **kwargs)`? – Steven Rumbalski Mar 31 '15 at 17:35
  • @StevenRumbalski yes, that would be necessary to also support any positional arguments, but I thought that would only muddy the waters at this point. – jonrsharpe Mar 31 '15 at 17:35
  • Jonrsharpe I need the time to think about your words and try to implement your idea ! – Michael Mar 31 '15 at 17:50
1
def myExample(**kwargs):
    if kwargs.get("width") and kwargs.get("w"):
        cmds.error("same flag used two times")
    else:
        myWidth = kwargs.get("width") or kwargs.get("w") or "nothing"
    print(myWidth)

myExample(width=200)

I'm used to do this in some of my scripts

EDIT ---

If you have to this multiple times, it may be easy to create a custom class :

class mimicKwargs:
    def __init__(self, labelLong, labelShort, defautValue,kwargDic):
        if kwargDic.get(labelLong) and kwargDic.get(labelShort):
            cmds.error("same flag used two times")
        else:
            self.userInput = kwargDic.get(labelLong) or kwargDic.get(labelShort) or defautValue
    def output(self):
        return self.userInput

def myExample(**kwargs):

    myWidth = mimicKwargs("width", "w", 150, kwargs).output()

    print(myWidth)

myExample(width=200)#
myExample()#print 150
DrWeeny
  • 2,487
  • 1
  • 14
  • 17