Printing variable label in function - python

How would I return a variable name in a function. E.g. If I have the function:
def mul(a,b):
return a*b
a = mul(1,2); a
b = mul(1,3); b
c = mul(1,4); c
This would return:
2
3
4
I would like it to return:
a = 2
b = 3
c = 4
How would I do this?

Unfortunately, you are unable to go "backwards" and print the name of a variable. This is explained in much further detail in this StackOverflow post.
What you could do is put the variable names in a dictionary.
dict = {"a":mul(1,2), "b":mul(1,3), "c":mul(1,4)}
From there you could loop through the keys and values and print them out.
for k, v in dict.items():
print(str(k) + " = " + str(v))
Alternatively, if you wanted your values ordered, you could put the values into a list of tuples and again loop through them in a for loop.
lst = [("a", mul(1,2)), ("b", mul(1,3)), ("c",mul(1,4))]

Here is how to do it with python-varname package:
from varname import varname
def mul(a, b):
var = varname()
return f"{var} = {a*b}"
a = mul(1,2)
b = mul(1,3)
c = mul(1,4)
print(a) # 'a = 2'
print(b) # 'b = 2'
print(c) # 'c = 2'
The package is hosted at https://github.com/pwwang/python-varname.
I am the author of the package. Let me know if you have any questions using it.

Related

Python: print variable name and value easily

I want to use a function that can automatically print out the variable and the value. Like shown below:
num = 3
coolprint(num)
output:
num = 3
furthermore, it would be cool if it could also do something like this:
variable.a = 3
variable.b = 5
coolprint(vars(variable))
output:
vars(variable) = {'a': 3, 'b': 5}
Is there any function like this already out there? Or should I make my own? Thanks
From Python 3.8 there is a = for f-strings:
#!/usr/bin/env python3
python="rocks"
print(f"{python=}")
This would output
# python=rocks
This lambda-based solution works well enough for me, though perhaps not in every case. It is very simple and only consumes one line.
coolprint = lambda *w: [print(x,'=',eval(x)) for x in w]
Exmaple..
coolprint = lambda *w: [print(x,'=',eval(x)) for x in w]
a, *b, c = [1, 2, 3, 4, 5]
coolprint('a')
coolprint('b','c')
coolprint('a','b','c')
coolprint('c','b','b','a','b','c')
which produces..
a = 1
b = [2, 3, 4]
c = 5
a = 1
b = [2, 3, 4]
c = 5
a = 1
b = [2, 3, 4]
c = 5
c = 5
b = [2, 3, 4]
b = [2, 3, 4]
a = 1
b = [2, 3, 4]
c = 5
An official way to accomplish this task sadly doesn't exist even though it could be useful for many people. What I would suggest and I have used it sometimes in the past is the following(I am not sure if this is the best way to do it).
Basically what you can do is to create a custom object that mimics one Python's data type per time. Bellow you can find an example for an integer.
class CustomVariable(object):
def __init__(self, name, value):
self.name = name
self.value = value
def __str__(self):
return "{} = {}".format(self.name, self.value)
def __add__(self, val) :
return self.value + val
myVar = CustomVariable("myVar", 15)
print myVar
myVar = myVar + 5
print myVar
Output:
myVar = 15
myVar = 20
Check the special method named "___str____"
I'm just appending my custom print which accepts a list or a dict of expressions, adding the globals at the lateral case (excuse the PEP violation, I just love single-liners, especially with lambdas):
pp = lambda p: print(' | '.join([f"{x} = {eval(x)}" for x, v in {**p, **dict(**globals())}.items() if not x.startswith('_')])) if type(p)==dict else print(' | '.join([f"{x} = {eval(x)}" for x in p if not x.startswith('_')]))
# Some Variables:
z = {'a':100, 'b':200}
a = 1+15
# Usage with dict & all the variables in the script:
pp(dict(**locals()))
# Usage with list, for specific expressions:
pp(['a', "z['b']", """ {x:x+a for x in range(95, z['a'])} """])
Note that f'{x =}' won't work correctly, since the evaluation takes scope inside the function.
I have discovered the answer is No. There is no way to do this. However, your best bet is something like this:
from pprint import pprint
def crint(obj, name):
if isinstance(obj, dict):
print '\n' + name + ' = '
pprint(obj)
else:
print '\n' + name + ' = ' + str(obj)
that way you can just do:
crint(vars(table.content[0]), 'vars(table.content[0])')
or:
j = 3
crint(j, 'j')

Storing every value of a changing variable

I am writing a piece of code that takes an input that varies according to discrete time steps. For each time step, I get a new value for the input.
How can I store each value as a list?
Here's an example:
"""when t = 0, d = a
when t = 1, d = b
when t = 2, d = c"""
n = []
n.append(d) #d is the changing variable
for i in range(t):
n.append(d)
What I expect to get is:
for t = 0, n = [a]; for t = 1, n = [a,b]; and for t = 2, n = [a,b,c]
What I actually get is:
for t = 0, n = [a], for t = 1, n = [b,b]; and for t = 2, n = [c,c,c]
See comment below, but based on the additional info you've provided, replace this:
n.append(d)
with this:
n.append(d[:])
Which type is the variable 'd'? If it is, for instance a list, the code you are showing pushes onto tbe list 'n' a reference to the variable 'd' rather than a copy of it. Thus, for each iteration of the loop you add a new reference of 'd' (like a pointer in C) to 'n', and when 'd' is updated all the entries in 'n' have, of course, the same value
To fix it you can modify the code so as to append a copy of 'd', either:
n.append(d[:])
n.append(list(d))
n.append(tuple(d))
You can simply do this
n = []
for i in range(t + 1):
n.append(chr(i+ord('a'))
And if you do not want to store the characters in the list rather some specific values which are related with d, then you have to change d in the for loop
n = []
d = 1
for i in range(t + 1):
n.append(d)
d += 2
It is difficult to say without seeing the code. But if d is not an int, this could happen. If d is a list for instance, it is passed by reference
n = []
d = [1]
n.append(d)
d[0] = 2
n.append(d)
print(n)
>>>> [[2], [2]]
So if each new d is just modified, your probleme arise. You can solve it by copying d :
from copy import copy
n = []
d = [1]
n.append(copy(d))
d[0] = 2
n.append(copy(d))
print(n)
>>>> [[1], [2]]
If you just wrap the variable inside an object you can watch what is being set to the variable by overriding __setattr__ method. A simple example.
class DummyClass(object):
def __init__(self, x):
self.history_of_x=[]
self.x = x
self._locked = True
def __setattr__(self, name, value):
self.__dict__[name] = value
if name == "x":
self.history_of_x.append(value)
d = DummyClass(4)
d.x=0
d.x=2
d.x=3
d.x=45
print d.history_of_x
Output :-
[4, 0, 2, 3, 45]

How to iterate over list-of-list, and accumulate product of two fields

I have written some simple Python code that should select the second element of each list within the main list and provide the user with a sum of all of these values. It should also multiply the second and third elements of each list within the main list and then sum these values.
However, when I attempt to run this code I am confronted with a
list index out of range
error.
usa_univs = [ ['California Institute of Technology',2175,37704],
['Harvard',19627,39849],
['Massachusetts Institute of Technology',10566,40732],
['Princeton',7802,37000],
['Rice',5879,35551],
['Stanford',19535,40569],
['Yale',11701,40500] ]
def total_enrollment(usa_univs):
a = 0
c = (len(usa_univs)) -1
while c > 0:
a = a + ([c][1])
b = b + (([c][1])*([c][2]))
c = c - 1
print (a,b)
print total_enrollment(usa_univs)
Instead of
[c][1]
or
[c][2]
You need to use this syntax:
usa_univs[c][1]
You forgot a usa_univs before [c][1] and the like. You also need to initialize b, otherwise you will get a reference-before-assignment error in b = b + ((usa_univs[c][1])*(usa_univs[c][2])).
Although not very elegant, this should work:
def total_enrollment(usa_univs):
a = 0
b = 0
c = (len(usa_univs)) -1
while c > 0:
a = a + (usa_univs[c][1])
b = b + ((usa_univs[c][1])*(usa_univs[c][2]))
c = c - 1
print (a,b)
You are creating a new list with just one element every time, then indexing that:
[c][1]
With only one element, that indexing will fail. If c is an index into the usa_univs list, you do need to reference the list:
usa_univs[c][1]
However, rather than manually manage indexes into that list, use a for loop to access each element directly:
a = 0
b = 0
for element in usa_univs:
a = a + element[1]
b = b + (element[1] * element[2])
where I also set b to 0 (you forgot that part), and c is no longer needed at all.
Python lets you unpack sequences into their constituent parts; you can do so even in the for loop:
for name, a_value, b_value in usa_univs:
a = a + a_value
b = b + (a_value * b_value)
and you can use augmented assignment to make adding to a and b a little more compact:
for name, a_value, b_value in usa_univs:
a += a_value
b += a_value * b_value
In the end your function should use return rather than printing. Leave printing to the caller; you already do so but your function now returns None. Your whole code, with all these improvements, then looks like:
usa_univs = [ ['California Institute of Technology',2175,37704],
['Harvard',19627,39849],
['Massachusetts Institute of Technology',10566,40732],
['Princeton',7802,37000],
['Rice',5879,35551],
['Stanford',19535,40569],
['Yale',11701,40500] ]
def total_enrollment(usa_univs):
a = 0
b = 0
for name, a_value, b_value in usa_univs:
a += a_value
b += a_value * b_value
return a, b
print total_enrollment(usa_univs)
As, it has be said you forgot to use usa_univs[c][1]
Also, use simple pythonic for to iterate over each element( without having to worry about the indexing), and access the individual element
def total_enrollment(usa_univs):
a = 0
b = 0
for data in usa_univs:
a += data[1]
b += data[1] * data[2]
return a,b
print (total_enrollment(usa_univs))

Manipulation of list of strings in python

How A can be changed into B in python?
A = ['a.png', 'b.png', 'c.png', 'd.png', 'e.png']
B = 'a.png;b.png;c.png;d.png;e.png'
Simple, use str.join():
>>> A = ['a.png', 'b.png', 'c.png', 'd.png', 'e.png']
>>> B = ';'.join(A)
>>> print(B)
a.png;b.png;c.png;d.png;e.png
there's a function called join which might come to your rescue now:
try this:
b = ';'.join(a)`
in your case:
A = ['a.png', 'b.png', 'c.png', 'd.png', 'e.png']
B = ';'.join(A)
print b #this will return -> a.png;b.png;c.png;d.png;e.png

Create a Python list filled with the same string over and over and a number that increases based on a variable.

I'm trying to create a list that is populated by a reoccurring string and a number that marks which one in a row it is. The number that marks how many strings there will be is gotten from an int variable.
So something like this:
b = 5
a = range(2, b + 1)
c = []
c.append('Adi_' + str(a))
I was hoping this would create a list like this:
c = ['Adi_2', 'Adi_3', 'Adi_4', 'Adi_5']
Instead I get a list like this
c = ['Adi_[2, 3, 4, 5]']
So when I try to print it in new rows
for x in c:
print"Welcome {0}".format(x)
The result of this is:
Welcome Adi_[2, 3, 4, 5]
The result I want is:
Welcome Adi_2
Welcome Adi_3
Welcome Adi_4
Welcome Adi_5
If anybody has Ideas I would appreciate it.
You almost got it:
for i in a:
c.append('Adi_' + str(i))
Your initial line was transforming the whole list a as a string.
Note that you could get rid of the loop with a list comprehension and some string formatting:
c = ['Adi_%s' % s for s in a]
or
c = ['Adi_{0}'.format(s) for s in a] #Python >= 2.6
Or as a list comprehension:
b = 5
a = range(2, b + 1)
c = ["Adi_" + str(i) for i in a]
Using list comprehensions:
b = 5
a = range(2, b + 1)
c = ['Adi_'+str(i) for i in a]
for x in c:
print"Welcome {0}".format(x)
Or all on one line:
>>> for s in ['Welcome Adi_%d' % i for i in range(2,6)]:
... print s
...
Welcome Adi_2
Welcome Adi_3
Welcome Adi_4
Welcome Adi_5

Categories

Resources