0

I'm just learning about **kwargs and I'm probably twisting up it's real use, so here goes...

I have 1 main script, 3 functions and a dict: script.py, setup(), billing(), newCust(), data = {}

setup() will call billing() and billing() will call newCust(), and newCust will return an id that will be used as setup() continues.

each function will accept optional named params, **kwargs, which is the dict, data = {}

im simply passing the dict along through the functions until it hits newCust(), where I manipulate the data and desire to return some other data. but it seems that newCust() is unable to return its data, when assigned to a variable. the data has been passing along fine.

             def setup(**kwargs):
                id = billing(**kwargs)
                analytics(id)

             ......

             def newCust(**kwargs):
                   username = kwargs.get("ok",None)
                   id = callapi(username)
                   return id

             def billing(**kwargs):
                   id = newCust(**kwargs)
                   return id

            ......
             #calling the setup function which will begin passing the data along
             data = {'ok':1, 'okk':2, 'okkk':3}
             setup(**data)
sirvon
  • 2,547
  • 1
  • 31
  • 55

1 Answers1

1

There are several ways for a function to "return" data:

  • using the return statement. If you have more that one value to return, user tuples (return (a, b, c)), or dicts (return {'customer': c, 'customer_data': d, 'whatever': e})

  • (I don't like this one) by exploiting the fact that mutable objects are passed to functions "by reference". Meaning that if a function receives an empty dict and populates it, the caller of that function will see the changes. One important caveat: if your function creates a new object with the same name, the reference is broken.

To illustrate my second point: consider the function:

def f(**kwargs):
    if 'data' in kwargs:
        data = kwargs['data'] #reference to data outside the function
    else:
        data = dict() #new object
    data['zero'] = 0
    data['zero_1'] = 0
    data['zeven'] = 7
    print('data is %s' % repr(data))

Here's how this works:

In[14]: data = {'name': 'Bond'}

In [15]: f()
data is {'zero': 0, 'zeven': 7, 'zero_1': 0}

In [16]: data
Out[16]: {'name': 'Bond'}

In [17]: f(data=data)
data is {'zero': 0, 'zeven': 7, 'name': 'Bond', 'zero_1': 0}

In [18]: data
Out[18]: {'zero': 0, 'zeven': 7, 'name': 'Bond', 'zero_1': 0}

Now, what happens when we create a new object?

def f(**kwargs):
    if 'data' in kwargs:
        data = dict(kwargs['data'])
        # A new object was created. This is a shallow copy
        # meaning that all its non-mutable objects are copied.
        # Mutable objects, on the other hand are still
        # referenced
        # See http://stackoverflow.com/q/17246693/17523 for
        # more details
    else:
        data = dict() #new object
        data['zero'] = [0, 0]
    data['zero'][0] = 'ZZEERROO'
    data['seven'] = 7
    data['last name'] = "Bond"
    print('data is %s' % repr(data))

As you may see, here f creates a new object, which is copy of the existing one. The non-mutable values were copied as is and calling f did not had side effect on the values of the existing object. The value that corresponded to key zero is a list (mutable). As the matter of fact, the value is reference to list, thus, when the dict was copied, this reference was copied too. That is why, f had side effects only on several values of data.

Boris Gorelik
  • 29,945
  • 39
  • 128
  • 170