6

I would like to do the following:

print "CC =",CC

but as a function so that i only have to write the variable CC once. I can't work out how to do this in a function as it always evaluates CC as a floating point number (which it is).... Is there a way to accept the input to a function as both a string and floating point number?

I tried this:

def printme(a): 
    b='%s' % a
    print b
    return b

but of course it only prints the value of a, not its name.

Tim Pietzcker
  • 328,213
  • 58
  • 503
  • 561
malby
  • 75
  • 1
  • 7

7 Answers7

3

You could use the inspect module (see also this SO question):

def printme(x):
    import inspect
    f = inspect.currentframe()
    val = f.f_back.f_locals[x]
    print x, '=', val


CC = 234.234    
printme('CC') # <- write variable name only once
# prints: CC = 234.234
Community
  • 1
  • 1
catchmeifyoutry
  • 7,179
  • 1
  • 29
  • 26
1

Perhaps a dictionary is a better approach to the problem. Assuming you have several name-value pairs that you want to use, you can put them in a dict:

params = {"CC": 1.2345, "ID": "Yo!", "foo": "bar"}

Then, for example, you could print all the names and values nicely formatted like this:

for key in params:
    print "{0} = {1}".format(key, params[key])

But since it is still unclear why you are trying to do this, it's hard to tell whether this is the right way.

Tim Pietzcker
  • 328,213
  • 58
  • 503
  • 561
  • 1
    The reason why is so that i only have to write CC once and not twice. I will be doing this alot so the shorthand is useful, the application i use python for is scientific computing so when coding mathematical algorithms you do this all the time to debug, in matlab for example this is very simple as you just type the variable name and matlab returns its value. – malby Jul 02 '12 at 11:24
  • 2
    +1 This is not what OP wants, but this is what OP should do. I've seen a lot of questions similar to this one on SO and every of them ends up with the decision that using of a dict is the most convenient and most pythonic way to solve the problem. – Vader Jul 02 '12 at 12:57
  • @Vader, I don't see how a this question is about string formatting. The problem of OP is how to *obtain* the variable name and value inside a function without providing *both* explicitly ("Is there a way to accept the input to a function as both a string and floating point number" and "... so that i only have to write the variable CC once"). – catchmeifyoutry Jul 02 '12 at 13:57
  • 2
    @catchmeifyoutry Sure this question is not about formatting it is about retrieving variable name. What I want to say is when you are trying to get variable name in Python then _you are probably doing something wrong_ and the best way to solve your problem is to stop using hacks and start using dicts instead. – Vader Jul 02 '12 at 14:44
  • @Vader ah ok then, we agree about the problem, just not about the solution (for this particular debugging use case) ;) Thanks for clarifying your point. – catchmeifyoutry Jul 02 '12 at 15:18
1

I think this is your required solution:

def printme(x): 
    keys_list = [key for key, value in globals().iteritems() if value == x]
    print keys_list
    for key in keys_list:
        if id(globals()[key]) == id(x):
            result = "%s = %s" %(key, x)
            print result
            break

    return result

for example if you declare a variable:

>>> c=55.6

then result of printme(c) will be

>>> 'c = 55.6'

Note: This solution is based on globally unique id matching.

Zubair Afzal
  • 2,016
  • 20
  • 29
  • this only works if the variable is in `globals`, meaning it is useless for function locals... – l4mpi Jul 02 '12 at 17:29
  • @l4mpi According to asked question a variable will be passed to a function. This means that, that variable will not be in the locals() of that function but in the globals(). So I checked globals() first. This could not be the exact solution but I think that It can give required solution by little modification i.e, check locals() first. – Zubair Afzal Jul 03 '12 at 05:03
  • this is wrong and demonstrates your poor understanding of variable scope. the variable will only be in `globals` if it is declared on the module level (or explicitly declared as global). if the variable is declared inside of another function, it will neither be in `globals` nor inside your print function's `locals` - try `def f(x): printme(x)` and then call `f(1)`. see catchmeifyoutrys answer for an actual solution using the locals of the calling scope. – l4mpi Jul 03 '12 at 08:26
0

If I understand you correctly you want something like this?

def f(a):
    print('{0}: = {1}'.format(locals().keys()[0], a))

Update:

I am aware that the example doesn't make a lot of sense, as it's basically the same as:

def f(a):
    print('a: {0}'.format(a))

I merely wanted to point the OP to locals() as I didn't quite understand what he's trying to accomplish.

I guess this is more what he's looking for:

def f(**kwargs):
    for k in kwargs.keys():
        print('{0}: {1}'.format(k, kwargs[k]))


f(a=1, b=2)
mfussenegger
  • 3,931
  • 23
  • 18
  • 1
    this won't work as the variable is not in the local scope of the function – l4mpi Jul 02 '12 at 10:46
  • This is exactly the same as writing `print 'a=%s' % a` as the local name will always be 'a'. If this really is what the OP wants, then it cannot be done because of the way Python works. – Jochen Ritzel Jul 02 '12 at 10:48
  • Yes almost, this results in printing the following a: = 0.166666666667, what i want to do is print the variable name as well so if i was to input f(x) i would like to get x = 0.166666666667 and if i was to input f(b) i would like to get b = 0.533 or whatever b is – malby Jul 02 '12 at 10:50
  • The second version requires the caller to use a keyword argument. It won't work with just a variable name. – Tim Pietzcker Jul 02 '12 at 10:58
0

Not exactly what you want, but easy to do:

def printme(**kwargs):
    for key, value in kwargs.items():
        print '%s=%s' % (key, value)
    return value

In [13]: printme(CC=1.23, DD=2.22)
CC=1.23
DD=2.22
Out[13]: 1.23
Tisho
  • 8,320
  • 6
  • 44
  • 52
0

If I understand you correctly you want a shorthand for printing a variable name and its value in the current scope? This is in general impossible without using the interpreters trace function or sys._getframe, which should in general only be used if you know what you're doing. The reason for this is that the print function has no other way of getting the locals from the calling scope:

def a():
    x = 1
    magic_print("x") #will not work without accessing the current frame

What you CAN do without these is explicitly pass the locals to a function like this:

def printNameAndValue(varname, values):
    print("%s=%s" % (varname, values[varname]))

def a():
    x = 1
    printNameAndValue("x", locals())  #prints 'x=1'

EDIT:

See the answer by catchemifyoutry for a solution using the inspect module (which internally uses sys._getframe). For completeness a solution using the trace function directly - useful if you're using python 2.0 and inspect isn't available ;)

from sys import settrace

__v = {} #global dictionary that holds the variables

def __trace(frame, event, arg):
    """ a trace function saving the locals on every function call """
    global __v 
    if not event == "call":
        return __trace
    __v.update(frame.f_back.f_locals)

def enableTrace(f):
    """ a wrapper decorator setting and removing the trace """
    def _f(*a, **kwa):
        settrace(__trace)
        try:
            f(*a, **kwa)
        finally:
            settrace(None)
    return _f

def printv(vname):
    """ the function doing the printing """
    global __v 
    print "%s=%s" % (vname, __v[vname])

Save it in a module and use like this:

from modulenamehere import enableTrace, printv

@enableTrace
def somefunction():
    x = 1
    [...]
    printv("x")
l4mpi
  • 5,103
  • 3
  • 34
  • 54
  • mmm, thanks for the effort, i think you are correct in that there is no solution, It seems to me now that my original solution is the best one, the difficulty arises due to python suppressing all inputs as default, actually is the a way of disabling this? – malby Jul 02 '12 at 11:37
  • No idea what you mean with 'suppressing all inputs', but see the answer by @catchmeifyoutry for a solution using inspect (which internally uses the trace function). – l4mpi Jul 02 '12 at 17:27
0

used a global variable to achieve this,func.__globals__.keys() contains all the variables passed to func, so I filtered out the name startin with __ and stored them in a list. with every call to func() the func.__globals__.keys() gets updated with the new variable name,so compare the new varn with the older glo results in the new variable that was just added.

glo=[]
def func(x):  
  global glo 
  varn=[x for x in func.__globals__.keys() if not x.startswith('__') and x!=func.__name__]
  new=list(set(varn)^set(glo))
  print("{0}={1}".format(new[0],x))
  glo=varn[:]

output:

>>> a=10
>>> func(a)
a=10
>>> b=20
>>> func(20)
b=20
>>> foo='cat'
>>> func(foo)
foo=cat
>>> bar=1000
>>> func(bar)
bar=1000
Ashwini Chaudhary
  • 244,495
  • 58
  • 464
  • 504