Variable name to string in Python - python

I have written some code in Python and I would like to save some of the variables into files having the name of the variable.
The following code:
variableName1 = 3.14
variableName2 = 1.09
save_variable_to_file(variableName1) # function to be defined
save_variable_to_file(variableName1)
should create 2 files, one named variableName1.txt and the other one variableName2.txt and each should contain the value of the corresponding variable at the time the function was called.
This variable name is supposed to be unique in the code.
Thanks!
HM

It is possible to find the name of a variable, a called function can get its caller's variables using:
import sys
def save_variable_to_file(var):
names = sys._getframe(1).f_globals
names.update(sys._getframe(1).f_locals)
for key in names.keys():
if names[key] == var:
break
if key:
open(key+".txt","w").write(str(var))
else:
print(key,"not found")
thing = 42
save_variable_to_file(thing)
But it is probably a really bad idea. Note that I have converted the value to a string, how would you want dictionaries and lists to be saved? How are you going to reconstruct the variable later?
import glob
for fname in glob.iglob("*.txt"):
vname = fname.rstrip(".txt")
value = open(fname).read()
exec(vname + "=" + value)
print(locals())
Using something like exec can be a security risk, it is probably better to use something like a pickle, JSON, or YAML.

No, this won't work. When you write the variable name there, it gives the value of the variable, 3.14 and so on, to save_variable_to_file(). Try one of these variants:
d = dict(my_variable_name=3.14)
save_variable_to_file('my_variable_name', d['my_variable_name'])
or:
var_name = 'my_variable_name'
d = {var_name: 3.14}
save_variable_to_file(var_name, d[var_name])
Here is a good tutorial, that you should definitely go through, if you're serious about learning Python.

Unfortunately it is not possible to find out the name of a variable. Either you extend your function to also allow a string as a parameter, or you have to use another solution:
save_variable_to_file("variableName1", variableName1)
Another solution would be to store your variables within a dict which allows the retrieval of the keys as well:
myVariables = {}
myVariables["variableName1"] = 3.14
for key, value in myVariables.items():
save_variable_to_file(key, value)

How about using a dict:
var_dict = {'variable1': 3.14, 'variable2':1.09}
for key, value in var_dict.items():
with open('path\%s'%key, "w") as file:
file.write(value)

for key, value in locals().items():
with open(key + '.txt', 'w') as f:
f.write(value)
should do your trick - as long as all locally defined variables are to be considered.
A far better solution, however, would be to put all you need into a dict and act as the others already proposed.
You could even do this:
def save_files(**files):
for key, value in files.items():
with open(key + '.txt', 'w') as f:
f.write(value)
and then
save_files(**my_previously_defined_dict)
save_files(**locals()) # as example above
save_files(filename='content')

Variables don't have names. When your script runs it creates one or more identifiers for some of the objects it interacts with commonly called variables, but these are temporary and cease to exist when the program ends. Even if you save them, you will then be faced with the reverse problem of how to turn them back into an identifier with saved name associated with the saved value.

If you want to get the name of your variable as string, use python-varname package (python3):
from varname import nameof
s = 'Hey!'
print (nameof(s))
Output:
s
Get the package here:
https://github.com/pwwang/python-varname

Related

How can I use f-string with a variable, not with a string literal?

I want to use f-string with my string variable, not with string defined with a string literal, "...".
Here is my code:
name=["deep","mahesh","nirbhay"]
user_input = r"certi_{element}" # this string I ask from user
for element in name:
print(f"{user_input}")
This code gives output:
certi_{element}
certi_{element}
certi_{element}
But I want:
certi_{deep}
certi_{mahesh}
certi_{nirbhay}
How can I do this?
f"..." strings are great when interpolating expression results into a literal, but you don't have a literal, you have a template string in a separate variable.
You can use str.format() to apply values to that template:
name=["deep","mahesh","nirbhay"]
user_input = "certi_{element}" # this string i ask from user
for value in name:
print(user_input.format(element=value))
String formatting placeholders that use names (such as {element}) are not variables. You assign a value for each name in the keyword arguments of the str.format() call instead. In the above example, element=value passes in the value of the value variable to fill in the placeholder with the element.
Unlike f-strings, the {...} placeholders are not expressions and you can't use arbitrary Python expressions in the template. This is a good thing, you wouldn't want end-users to be able to execute arbitrary Python code in your program. See the Format String Syntax documenation for details.
You can pass in any number of names; the string template doesn't have to use any of them. If you combine str.format() with the **mapping call convention, you can use any dictionary as the source of values:
template_values = {
'name': 'Ford Prefect',
'number': 42,
'company': 'Sirius Cybernetics Corporation',
'element': 'Improbability Drive',
}
print(user_input.format(**template_values)
The above would let a user use any of the names in template_values in their template, any number of times they like.
While you can use locals() and globals() to produce dictionaries mapping variable names to values, I'd not recommend that approach. Use a dedicated namespace like the above to limit what names are available, and document those names for your end-users.
If you define:
def fstr(template):
return eval(f"f'{template}'")
Then you can do:
name=["deep","mahesh","nirbhay"]
user_input = r"certi_{element}" # this string i ask from user
for element in name:
print(fstr(user_input))
Which gives as output:
certi_deep
certi_mahesh
certi_nirbhay
But be aware that users can use expressions in the template, like e.g.:
import os # assume you have used os somewhere
user_input = r"certi_{os.environ}"
for element in name:
print(fstr(user_input))
You definitely don't want this!
Therefore, a much safer option is to define:
def fstr(template, **kwargs):
return eval(f"f'{template}'", kwargs)
Arbitrary code is no longer possible, but users can still use string expressions like:
user_input = r"certi_{element.upper()*2}"
for element in name:
print(fstr(user_input, element=element))
Gives as output:
certi_DEEPDEEP
certi_MAHESHMAHESH
certi_NIRBHAYNIRBHAY
Which may be desired in some cases.
If you want the user to have access to your namespace, you can do that, but the consequences are entirely on you. Instead of using f-strings, you can use the format method to interpolate dynamically, with a very similar syntax.
If you want the user to have access to only a small number of specific variables, you can do something like
name=["deep", "mahesh", "nirbhay"]
user_input = "certi_{element}" # this string i ask from user
for element in name:
my_str = user_input.format(element=element)
print(f"{my_str}")
You can of course rename the key that the user inputs vs the variable name that you use:
my_str = user_input.format(element=some_other_variable)
And you can just go and let the user have access to your whole namespace (or at least most of it). Please don't do this, but be aware that you can:
my_str = user_input.format(**locals(), **globals())
The reason that I went with print(f'{my_str}') instead of print(my_str) is to avoid the situation where literal braces get treated as further, erroneous expansions. For example, user_input = 'certi_{{{element}}}'
I was looking for something similar with your problem.
I came across this other question's answer: https://stackoverflow.com/a/54780825/7381826
Using that idea, I tweaked your code:
user_input = r"certi_"
for element in name:
print(f"{user_input}{element}")
And I got this result:
certi_deep
certi_mahesh
certi_nirbhay
If you would rather stick to the layout in the question, then this final edit did the trick:
for element in name:
print(f"{user_input}" "{" f"{element}" "}")
Reading the security concerns of all other questions, I don't think this alternative has serious security risks because it does not define a new function with eval().
I am no security expert so please do correct me if I am wrong.
This is what you’re looking for. Just change the last line of your original code:
name=["deep","mahesh","nirbhay"]
user_input = "certi_{element}" # this string I ask from user
for element in name:
print(eval("f'" + f"{user_input}" + "'"))

Unable to store non-characters text in a dictionary in python

I am trying sentiment analysis where I have data like
source_text-> #LiesbethHBC I have a good feeling actually 🙈 its not that long, it's pretty soon!\nAw you deserve these tickets
then! 💖
result_value-> Sentiment(polarity=0.0, subjectivity=0.0)
I want to store this key value pair in a python dictionary.
I tried creating one as:
dict={}
dict[source_text].append(result_value)
but I get KeyError
Is there a way to store such text(just not characters) in a dictionary?
Your problem has nothing to do with "non-character text" (which doesn't mean anything actually), the only requirement for an object to be usable as a dict key is that it's hashable, and there's absolutely no restriction on what you can use as value.
Your problem quite simply comes from the fact that you're trying to get the value for an inexistant key (that's what KeyError means : the key you ask for does not exist in the dict).
Here :
mydict = {}
at this point, mydict is empty so just any item access will raise a KeyError
then you're doing this:
dict[source_text].append(result_value)
which is basically:
something = mydict[source_text] # get value for key `source_text`
something.append(result_value)
Since your dict is empty, the first line WILL obviously raise a KeyError.
If you want to store one unique result_value for each source_text value then the proper syntax is:
mydict[source_text] = result_value
If you want to store a list of result_value for each source_text value then you have to either explicitely test if the key is set, if not set it with an empty list, then append to this list:
if source_text not in mydict:
mydict[source_text] = []
mydict[source_text].append(result_value)
or just use a DefaultDict instead:
from collections import DefaultDict
mydict = DefaultDict(list)
# DefaultDict will automagically create the key with an empty list
# as value if the key is missing
mydict[source_text].append(result_value)
Now I strongly suggest that you invest some time in properly learning Python (hint: there's a quite decent tutorial in the official documentation) if you have to use it, this will save on everyone's time.
The problem is that when you tried to pull out the key #LiesbethHBC I have a good feeling actually 🙈 its not that long, it's pretty soon!\nAw you deserve these tickets then! 💖 in the dictionary which in this case is non-existent, Python gave you a KeyError meaning that the key didn't exist in the dictionary. A simple way to solve this is by initially checking whether you have that particular key in the dictionary, if yes, do whatever you wanna do with it, else create that key first.
By the way, avoid using dict (dictionary datatype) or any other datatypes as a variable name.
This is what you should actually do:
dictionary = {} # Since, 'dict' is the dictionary data-type in Python
if (source_text in dictionary):
# If the key exists...
dictionary[source_text].append(result_value)
else:
# If the key does not exist...
dictionary[source_text] = []
This should help...
Have you tried using '.update' method?
dict = {}
dict.update({'First':'Test'})
dict.update({'Lets Get':'Real'})
print (dict)
Output:
{'Testing': 'Dictionaries', 'Lets Get': 'Real'}
EDIT:
Or even:
dict = {}
dict.update({'Polarity':0.91})
dict.update({'Subjectivity':0.73})
print (dict)
Output:
{'Polarity': 0.8, 'Subjectivity': 0.73}

Using A Python List or String in Dictionary Lookup?

Use Case
I am making a factory type script in Python that consumes XML and based on that XML, returns information from a specific factory. I have created a file that I call FactoryMap.json that stores the mapping between the location an item can be found in XML and the appropriate factory.
Issue
The JSON in my mapping file looks like:
{
"path": "['project']['builders']['hudson.tasks.Shell']",
"class": "bin.classes.factories.step.ShellStep"
}
path is where the element can be found in the xml once its converted to a dict.
class is the corresponding path to the factory that can consume that elements information.
In order to do anything with this, I need to descend into the dictionaries structure, which would look like this if I didn't have to draw this information from a file(note the key reference = 'path' from my json'):
configDict={my xml config dict}
for k,v in configDict['project']['builders']['hudson.tasks.Shell'].iteritems():
#call the appropriate factory
The issue is that if I look up the path value as a string or a list, I can not use it in 'iteritems'():
path="['project']['builders']['hudson.tasks.Shell']" #this is taken from the JSON
for k,v in configDict[path].iteritems():
#call the appropriate factory
This returns a key error stating that I can't use a string as the key value. How can I used a variable as the key for that python dictionary?
You could use eval:
eval( "configDict"+path )
You can use the eval() function to evaluate your path into an actual dict object vs a string. Something like this is what I'm referring to:
path="['project']['builders']['hudson.tasks.Shell']" #this is taken from the JSON
d = eval("configDict%s" % path)
for k,v in d.iteritems():
#call the appropriate factory

Python: Transform a string into a variable and its value

I am relatively new on Python.
The program I am writing reads line by line a XML file using a while loop. The data read is split so the information that I get is something like:
datas = ['Name="Date"', 'Tag="0x03442333"', 'Level="Acquisition"', 'Type="String"']
-Inside my program, I want to assign to some variables called exactly as the word before the = sign, the information after the = sign in the previous strings. And then I will introduce them as attributes for a class (this already works)
- What I have done until the moment is:
Name = ''
Tag = ''
Level = ''
Type = ''
for i in datas:
exec(i)
-It works fine that way. However, I do not want to use the exec function. Is there any other way of doing that?
Thank you
exec is generally the way to go about this. You could also add it to the globals() dictionary directly, but this would be slightly dangerous sometimes.
for pair in datas:
name, value = pair.split("=")
globals()[name] = eval(value)
You are right that you should avoid exec for security reasons, and you should probably keep the field values in a dict or similar structure. It's better to let a Python library do the whole parsing. For example, using ElementTree:
import xml.etree.ElementTree as ET
tree = ET.parse('myfile.xml')
root = tree.getroot()
and then iterating over root and its children, depending on how exactly your XML data looks like.
At most what you expect to do is discussed here. To convert string to variable name.
But what you should ideally do is to create a dictionary. Like this.
for i in datas:
(key,value)=i.split("=")
d[key] = eval(value)
NOTE: Still avoid using eval.
As Pelle Nilsson says, you should use a proper XML parser for this. However, if the data is simple and the format of your XML file is stable, you can do it by hand.
Do you have a particular reason to put this data into a class? A dictionary may be all you need:
datas = ['Name="Date"', 'Tag="0x03442333"', 'Level="Acquisition"', 'Type="String"']
datadict = {}
for s in datas:
key, val = s.split('=')
datadict[key] = val.strip('"')
print(datadict)
output
{'Tag': '0x03442333', 'Type': 'String', 'Name': 'Date', 'Level': 'Acquisition'}
You can pass such a dictionary to a class, if you want:
class MyClass(object):
def __init__(self, data):
self.__dict__ = data
def __repr__(self):
s = ', '.join('{0}={1!r}'.format(k,v) for k, v in self.__dict__.items())
return 'Myclass({0})'.format(s)
a = MyClass(datadict)
print(a)
print(a.Name, a.Tag)
output
Myclass(Tag='0x03442333', Type='String', Name='Date', Level='Acquisition')
Date 0x03442333
All of the code in this answer should work correctly on any recent version of Python 2 as well as on Python 3, although if you run it on Python 2 you should put
from __future__ import print_function
at the top of the script.

for k, v in dict ///// TypeError: 'str' object is not callable

import getpass
class LogInNow(object):
def __init__(self, file):
self.openfile = open(file, 'r')
self.readfile = self.openfile.read()
def authenticate(self):
self.username = raw_input("Enter Username> ")
self.password = getpass.getpass("Enter Password> ")
for k, v in self.readfile(): <---------------------------------
if k == self.username and v == self.password:
print "It worked"
else:
print "Fail"""
go = LogInNow("password.txt")
go.authenticate()
This is a crude 'log-in' module that iterates through a dict located on password.txt and for now, exclusively displays messages based on match success.
The error is displayed in the title, and the erroneous line is the one with the arrow.
I have tried removing parentheses as well as all kinds of other stuff that I don't remember
Any ideas?
Besides the other other issues (see below), you're setting self.readfile to a string, not a function. You're accidentally calling self.openfile.read() instead of grabbing the function itself. You wanted:
self.readfile = self.openfile.read # <-- No parentheses at the end!
You've got a variety of other problems as well. For instance, the return value of the read method is a string, not a mapping or whatever sequence you're expecting, so the for k, v in self.readfile() won't work. You'll need to first parse the string into a dictionary, or sequence of tuples, or whatever you expect, somehow--how you do that depends on your requirements and the contents of the file, of course. As a side note, if you do want a dictionary (say, mydict) you should take the loop out in the first place and just check whether mydict.get(self.username) == self.password.
You've also got your looping line (the one you marked as the error) indented too little, though I suspect that's a copy/paste error.
When you defined readfile, you already called the read function, so readfile is set to the return value of that function --- namely, a string containing the contents of the file. You can't call that string. Either change your loop to for k, v in self.readfile or change your earlier line to self.readfile = self.openfile.read.
However, your code still won't work, because you're trying to iterate over the entire file as if it were a serquence of key-value pairs. But it's not; it's just one big string. If you want to parse it into key-value pairs, you need to do that yourself somehow.

Categories

Resources