Shyam Sunder
Shyam Sunder

Reputation: 1240

Actual name of the passed parameter

Is it possible to get the name of the passed parameter in Python?

Specifically, I want to be able to do this:

def a(name):
    print "the actual passed parameter is %s" %(???)

What it solves?

So if I need to distribute coins in bags and baskets, say bag_list and basket_list, I need not send an explicit identifier saying 'bag' or 'basket'

Which is how I've currently worked around it. I'm currently using a (global) dictionary for this purpose-

ID = {'bag': bag_list, 'basket': basket_list}

def a(name, id):
   dist_list = ID[id]

It also boils down to converting a variable name to string because the desired behavior can also be modeled as:

def a(name):
    name = ???get variable name as string magically???
    if name.startswith('bag'): 
         dist_list = ID['bag']
    else:
         dist_list = ID['basket']

The re-modeled question has been answered comprehensively in https://stackoverflow.com/a/9121991/1397945 but I'm re-posting this

Thanks.

Upvotes: 0

Views: 145

Answers (4)

steveha
steveha

Reputation: 76695

In general, what you want is not possible, because Python doesn't pass objects with names attached.

In the case of calling a function, you can get a dictionary with name/value pairs, if the user always calls the function with keyword arguments.

So you can do this:

def a(**kwargs):
    if len(kwargs) != 1:
        raise ValueError("only pass a single keyword arg starting with 'bag' or 'basket'")
        # Above line is Python 3.x syntax; below line is Python 2.x syntax
        # raise ValueError, "only pass a single keyword arg starting with 'bag' or 'basket'"
    name, value = list(kwargs.items())[0]
    if name.startswith('bag'): 
         dist_list = ID['bag']
    else:
         dist_list = ID['basket']
    # I don't know what you want next, but let's say you want to append:
    dist_list.append(value)

Then the user must call the function like so:

a(bag=3)
a(basket=5)

EDIT: the code is now correct for Python 3.x, and can be easily modified for Python 2.x (see comment).

EDIT: Now that I think about it, we can generalize this. I'm going to rename ID as _containers.

def a(**kwargs):
    for key, value in kwargs.items():
        if key in _containers:
            _containers[key].append(value)
        else:
            raise ValueError("invalid container name '%s'" % key)

Now you can append multiple containers with a single call. But it's a bit tricky because the user might do something like this:

a(bag=1, basket=2, bag=3)

The bag=1 will never happen because bag will be set to 3 in the dictionary. (There might be a tricky way to replace the dictionary with a custom class with overloaded functions, but that way lies madness... I don't recommend code that tricky.)

It's not as nice, but if you want names starting with bag or whatever to work, this will do it:

def a(**kwargs):
    for key, value in kwargs.items():
        found = False
        for container_name in _containers:
            if key.startswith(container_name):
                _containers[container_name].append(value)
                found = True
                break
        if not found:
            raise ValueError("no container name matched '%s'" % key)

Upvotes: 1

Thorsten Kranz
Thorsten Kranz

Reputation: 12755

It is possible - but I'd regard this rather as a dirty hack. After reading my solution, you should step back and think twice if you really want to go this way.

def a(name):
    global_variables = globals()
    try:
        name_of_passed_in_variable = [x for x in global_variables if id(global_variables[x]) == id(name)][0]
    except Exception:
        name_of_passed_in_variable = "unknown"
    print name_of_passed_in_variable, name

my_name = "Tom"
a(my_name)

a("Mike")

second_name = my_name
a(second_name)

Will give the output:

my_name Tom
unknown Mike
my_name Tom

With my examples, you can also see the drawbacks of this approach.

  • If you pass in a string directly without assigning a variable, you cannot determine the name
  • If you have multiple references to the same variable, you'll never know which name will be picked up

For the latter problem you could of course implement some logic to cope with it - or maybe this is no problem for you at all.

Upvotes: 3

Alex L
Alex L

Reputation: 8925

Would something like this suit your use case?

>>> class Inventory(object):
    basket = 0
    bag = 0
    def add_coins(self, **kwargs):
        if 'bag' in kwargs:
            self.bag += kwargs['bag']
        if 'basket' in kwargs:
            self.basket += kwargs['basket']


>>> i = Inventory()
>>> i.add_coins(bag=6)
>>> i.bag
6
>>> i.add_coins(bag=2)
>>> i.bag
8
>>> i.basket
0

Upvotes: 1

milkypostman
milkypostman

Reputation: 3043

I think the problem you have is the differences in arguments. There are keyword arguments where the name is explicitly stated by the caller and simple positional arguments. You could do this,

def a(**kwargs):
    if 'bag' in kwargs:
         dist_list = ID['bag']
    else:
        dist_list = ID['basket']

and you would have to pass variables like so,

a(bag=...)

or if you wanted to see if the parameter startswith bag or basket you could do,

def a(**kwargs):
    if any(k.startswith('bag') for k in kwargs.iterkeys()):
        dist_list = ID['bag']
    elif any(k.startswith('basket') for k in kwargs.iterkeys()):
        dist_list = ID['basket']
    else:
        raise Exception('a requires argument starting with `basket` or `bag`')

but then you have the problem of determining which key starts with bag or basket, so I would probably do,

def a(**kwargs):
    valid_key = [k for k in kwargs.iterkeys() if k.startswith('bag') or k.startswith('basket')][0]
    if valid.key.startswith('bag'):
         dist_list = ID['bag']
    elif valid.key.startswith('basket'):
        dist_list = ID['basket']
    else:
        raise Exception('a requires argument starting with `basket` or `bag`')

Upvotes: 2

Related Questions