7
def foo(name, *args, **kwargs):

I need to remove the first two arguments of *args in case its length (len(args)) is greater than two. Is it possible to do that?

Here is why I need to do that: I've got the following function:

def foo(name, xml='my.xml', xsd='my.xsd', *otherfiles):
    print('name: ' + name)
    print('xml: ' + xml)
    print('xsd: ' + xsd)
    print(otherfiles)

I need to add an optional boolean parameter to the arguments of this function, without breaking the backward compatibility. So I change the function to this:

def foo2(name, *otherfiles, **kwargs):
    kwargs.setdefault('relativePath', True)
    if len(otherfiles)>0:
        xml = otherfiles[0]
    else:
        kwargs.setdefault('xml', 'my.xml')
        xml = kwargs['xml']

    if len(otherfiles)>1:
        xsd = otherfiles[1]
    else:
        kwargs.setdefault('xsd', 'my.xsd')
        xsd = kwargs['xsd']
    print('name: ' + name)
    print('xml: ' + xml)
    print('xsd: ' + xsd)
    print(otherfiles)

Now I test the backward compatibility by checking if the output of foo and foo2 is the same:

foo('my_name', '../my.xml', '../my.xsd', 'file1', 'file2')
print('===============================================================')
foo2('my_name', '../my.xml', '../my.xsd', 'file1', 'file2')

The output:

name: my_name  
xml: ../my.xml  
xsd: ../my.xsd  
('file1', 'file2')  
===============================================================  
name: my_name  
xml: ../my.xml  
xsd: ../my.xsd  
('../my.xml', '../my.xsd', 'file1', 'file2')

As you can see, the first two items of otherfiles should be removed to maintain the old functionality.

B Faley
  • 17,120
  • 43
  • 133
  • 223

2 Answers2

4

We can not remove items from the args because it is tuple. As tuple is immutable

but we can create new variable which contains items from the args according to our logic. And we can use new variable to next process.

Demo1:

def foo(name, *args, **kwargs):
    print "args: ", args
    print "Type of args: ", type(args)
    if len(args)>2:
        tmp_args = args[0], args[1]
    else:
        tmp_args = args

    print "Temp args:", tmp_args


print "Debug 1:"    
foo("ww", 12,3,4,4,5,6,7,7)
print "\nDebug 2:"
foo("ss", 1)


Debug 1:
args:  (12, 3, 4, 4, 5, 6, 7, 7)
Type of args:  <type 'tuple'>
Temp args: (12, 3)

Debug 2:
args:  (1,)
Type of args:  <type 'tuple'>
Temp args: (1,)

We can overwrite same variable name if we not need values from the variable in next process,

Demo 2

def foo(name, *args, **kwargs):
    print "args: ", args
    print "Type of args: ", type(args)
    if len(args)>2:
        args = args[0], args[1]     #- Created Same name variable.
    print "Temp args:", args
Vivek Sable
  • 9,938
  • 3
  • 40
  • 56
2

The following could be helpful to alter named arguments list before a function call.

Add an argument:

# The wrapper adds a named argument 'age' to the function call
def decorator(func):
    def wrapper(*args, **kwargs):
        print("before")
        # Add an argument to the existing list
        kwargs.update(age=4)
        func(*args, **kwargs)
        print("after")
    return wrapper

# Decorator replaces myFunc by a function which adds/changes the
# age argument before calling original myFunc
@decorator 
def myFunc(name, age):
    print(f"Hi {name} ! Are you {age} ?")
    

myFunc("Roger")
myFunc(name="Marc", age=33)

Remove elements:

# The wrapper removes the inappropriate arguments from kwargs
def decorator(func):
    def wrapper(*args, **kwargs):
        print("before")
        # Keep only expected names
        # kwargs.pop("argname") works if we know the name to remove
        new_kwargs = {k: v for k, v in kwargs.items() if k in ["name"]}
        func(*args, **new_kwargs)
        print("after")
    return wrapper

# Decorator replaces myFunc by a function which filters
# argument(s) before calling original myFunc
@decorator def myFunc(name):
    print(f"Hi {name} !")

    
myFunc(tutu=40, name="Roger", toto="eeee")

It does not strictly answer the question but it fits with the title and somebody asked about kwargs in comments of the accepted answer.

RFen
  • 141
  • 1
  • 7