Python3 - Dynamically calling variables in a loop and writing into file - python-3.x

I have some of variables I would like to write in a file. Since they are pretty much well ordered (01,12 etc) I want to put them inside a loop.
For that, one needs to convert the variable result into a string. The problem is that if I do that, I'll write the very same string that is my variable name (and not my variable result, converted to string).
r01=func_dif(t0g,t1g)
r23=func_dif(t2g,t3g)
r34=func_dif(t3g,t4g)
r45=func_dif(t4g,t5g)
r56=func_dif(t5g,t6g)
r67=func_dif(t6g,t7g)
a = 0
b = 1
with open('script2Out', 'w') as f:
for a in range(7):
f.write('r{}{} = {} \n'.format(a,b,str(f'r{a}{b}')))
a += 1
b += 1
As you have probably guessed, here is what I get
r01 = r01
r12 = r12
r23 = r23
r34 = r34
r45 = r45
r56 = r56
r67 = r67
Whereas I wish to get something like (my variable results are lists)
r01 = [5.24,21,74,95,66]
...

You can do this using python's eval() function:
a = 0
b = 1
with open('script2Out', 'w') as f:
for a in range(7):
f.write('r{}{} = {} \n'.format(a,b,str(eval('r{a}{b}'))))
b += 1

Related

How can I store a variable in a list number?

I have a piece of code which uses a counter to move through a list but the problem is that when I do counter+1 it does not store in the variable counter.
code:
list = [1,2,3,4,5]
counter = 0
b = list[counter]+list[counter+1]
print(counter)
I expect this to return 1 since I added 1 to the counter but instead it returns 0 as if i had not add anything.
As joshmeranda said, counter + 1 does not change the counter variable.
You'd probably have to separately increment the counter variable, like:
list = [1,2,3,4,5]
counter = 0
b = list[counter]+list[counter+1]
counter += 1 #variable += 1 is syntastic sugar for variable = variable + 1
print(counter)

How to append values in a while loop by using an if statement

records = [["chi", 20.0],["beta", 50.0],["alpha", 50.0]]
a = len(records)
i = 0
b = []
while i < a:
print(records[i][1])
b.append(records[i][1])
i = i + 1
print(b)
c = len(b)
#from previous question
unique_list = []
for el in b:
if el not in unique_list:
unique_list.append(el)
else:
print ("Element already in the list")
print(unique_list)
second_lowest_score = unique_list[1]
print(second_lowest_score)
names_list = []
g = 0
while g < a:
if records[g][1] == second_lowest_score:
names_list.append(records[g][0])
g = g + 1
print(names_list)
What I want to do is to append the names of records which have the second lowest score (50) to the names_list. However, the while loop gives me no result. There is no syntax error so I am not sure why my code is wrong. However, when I use the previous while loop for appending the numbers, it seems to work fine. Is it not possible to use an if statement in a while loop?
This is a pretty simple problem. The g variable is not getting incremented if the if statement does not run, so the loop will endlessly continue on the same value of g.
The fix for this is to move the increment of g outside of the if statement (but still in the while loop). That way it will continue past values even if they do not match the if condition.
if records[g][1] == second_lowest_score:
names_list.append(records[g][0])
g = g + 1

reduce the number of IF statements in Python

I have written a function that is going to have up to 72 IF statements
and i was hoping to write code that will be much shorter, but have no idea where to start
The function reads the self.timeselect variable when a radio button is selected and the result is saved to a text file called missing_time.txt. If the result is equal to 1 then save "0000" to the file, if the result is 2 save then 0020 to the text file etc. This can be for 72 possible combinations.
Is there a smarter way to simplify the function ?
def buttonaction():
selectedchoice = ""
if self.timeselect.get() == 1:
selectedchoice = "0000"
orig_stdout = sys.stdout
f = open('missing_time.txt', 'w')
sys.stdout = f
print(selectedchoice)
f.close()
if self.timeselect.get() == 2:
selectedchoice = "0020"
orig_stdout = sys.stdout
f = open('missing_time.txt', 'w')
sys.stdout = f
print(selectedchoice)
f.close()
self.timeselect = tkinter.IntVar()
self.Radio_1 = tkinter.Radiobutton(text="0000",variable =
self.timeselect,indicator = 0 ,value=1)
self.Radio_1.place(x=50,y=200)
self.Radio_2 = tkinter.Radiobutton(text="0020",variable =
self.timeselect,indicator = 0 ,value=2)
self.Radio_2.place(x=90,y=200)
choice_map = {
1 : "0000",
2 : "0020"
}
def buttonaction():
selected = self.timeselect.get()
if 0 < selected < 73: # This works as intended in Python
selectedchoice = choice_map[selected]
# Do you intend to append to file instead of replacing it?
# See text below.
with open("missing_time.txt", 'w') as outfile:
outfile.write(selectedchoice + "\n")
print(selectedchoice)
Better yet, if there is a pattern that relates the value of self.timeselect.get() to the string that you write out, generate selectchoice directly from that pattern instead of using a dictionary to do the mapping.
Edit
I find it a bit odd that you are clearing the file "missing_time.txt" every time you call buttonaction. If your intention is to append to it, change the file mode accordingly.
Also, instead of opening and closing the file each time, you might just want to open it once and pass the handler to buttonaction or keep it as a global depending on how you use it.
Finally, if you do not intend to catch the KeyError from an invalid key, you can do what #Clifford suggests and use choice_map.get(selected, "some default value that does not have to be str").
All you need to do in this case is construct a string from the integer value self.timeselect.get().
selectedchoice = self.timeselect.get()
if 0 < selectedchoice < 73:
orig_stdout = sys.stdout
f = open('missing_time.txt', 'w')
sys.stdout = f
print( str(selectedchoice).zfill(4) ) # Convert choice to
# string with leading
# zeros to 4 charaters
f.close()
Further in the interests of simplification, redirecting stdout and restoring it is a cumbersome method of outputting to a file. Instead, you can write directly to the file:
with open('missing_time.txt', 'w') as f:
f.write(selectedchoice + "\n")
Note that because we use the with context manager here, f is automatically closed when we leave this context so there is no need to call f.close(). Ultimately you end up with:
selectedchoice = self.timeselect.get()
if 0 < selectedchoice < 73:
with open('missing_time.txt', 'w') as f:
f.write( str(selectedchoice).zfill(4) + "\n" )
Even if you did use the conditionals each one differs only in the first line, so only that part need be conditional and the remainder of the content performed after the conditionals. Moreover all conditionals are mutually exclusive so you can use else-if:
if self.timeselect.get() == 1:
selectedchoice = "0000"
elif self.timeselect.get() == 2:
selectedchoice = "0020"
...
if 0 < selectedchoice < 73:
with open('missing_time.txt', 'w') as f:
f.write(selectedchoice + "\n")
In circumstances where there is no direct arithmetic relationship between selectchoice and the required string, or the available choices are perhaps not contiguous, it is possible to implement a switch using a dictionary:
choiceToString = {
1: "0001",
2: "0002",
...
72: "0072",
}
selectedchoice = choiceToString.get( self.timeselect.get(), "Invalid Choice")
if selectedchoice != "Invalid Choice":
with open('missing_time.txt', 'w') as f:
f.write(selectedchoice + "\n")
Since there is no switch statement in Python, you can't really reduce the number of if statements. But I see 2 two way to optimize and reduce your code length.
First, you can use some
if condition:
elif condition:
instead of
if condition:
if condition:
since you can't have self.timeselect.get() evaluated to more than one int.
Secondly you can wrap all the code that doesn't vary in a function.
You can get rid of selectedchoice and put
orig_stdout = sys.stdout
f = open('missing_time.txt', 'w')
sys.stdout = f
print(selectedchoice)
f.close()
in a function writeToFile(selectedOption)
I'm assuming that the values are arbitrary and there's no defined pattern. I also see that the only thing that changes in your code is the selectedChoice variable. You can use a Dictionary in such cases. A dictionary's elements are key/value pairs so you can reference the key and get the value.
dictionary = {
1:"0000",
2:"0020",
3:"0300",
4:"4000"
}
def buttonAction():
selectedChoice = dictionary[self.timeselect.get()]
if 0<selectedChoice<=72:
f=open('missing_time.txt','w')
f.write(selectedChoice+" ")
f.close()
print(choice)

How to wrap a Python text stream to replace strings on the fly?

Given how convoluted my solution seems to be, I am probably doing it all wrong.
Basically, I am trying to replace strings on the fly in a text stream (e.g. open('filename', 'r') or io.StringIO(text)). The context is that I'm trying to let pandas.read_csv() handle "Infinity" as "inf" instead of choking on it.
I do not want to slurp the whole file in memory (it can be big, and even if the resulting DataFrame will live in memory, no need to have the whole text file too). Efficiency is a concern. So I'd like to keep using read(size) as the main way to get text in (no readline which is quite slower). The difficulty comes from the cases where read() might return a block of text that ends in the middle of one of the strings we'd like to replace.
Anyway, below is what I've got so far. It handles the conditions I've thrown at it so far (lines longer than size, search strings at the boundary of some read block), but I'm wondering if there is something simpler.
Oh, BTW, I don't handle anything else than calls to read().
class ReplaceIOFile(io.TextIOBase):
def __init__(self, iobuffer, old_list, new_list):
self.iobuffer = iobuffer
self.old_list = old_list
self.new_list = new_list
self.buf0 = ''
self.buf1 = ''
self.sub_has_more = True
def read(self, size=None):
if size is None:
size = 2**16
while len(self.buf0) < size and self.sub_has_more:
eol = 0
while eol <= 0:
txt = self.iobuffer.read(size)
self.buf1 += txt
if len(txt) < size:
self.sub_has_more = False
eol = len(self.buf1) + 1
else:
eol = self.buf1.rfind('\n') + 1
txt, self.buf1 = self.buf1[:eol], self.buf1[eol:]
for old, new in zip(self.old_list, self.new_list):
txt = txt.replace(old, new)
self.buf0 += txt
val, self.buf0 = self.buf0[:size], self.buf0[size:]
return val
Example:
text = """\
name,val
a,1.0
b,2.0
e,+Infinity
f,-inf
"""
size = 4 # or whatever -- I tried 1,2,4,10,100,2**16
with ReplaceIOFile(io.StringIO(text), ['Infinity'], ['inf']) as f:
while True:
buf = f.read(size)
print(buf, end='')
if len(buf) < size:
break
Output:
name,val
a,1.0
b,2.0
e,+inf
f,-inf
So for my application:
# x = pd.read_csv(io.StringIO(text), dtype=dict(val=np.float64)) ## crashes
x = pd.read_csv(ReplaceIOFile(io.StringIO(text), ['Infinity'], ['inf']), dtype=dict(val=np.float64))
Out:
name val
0 a 1.000000
1 b 2.000000
2 e inf
3 f -inf

Convert ALL UPPERCASE to Title

#!/usr/bin/python3.4
import urllib.request
import os
import re
os.chdir('/home/whatever/')
a = open('Shopstxt.csv','r')
b = a.readlines()
a.close()
c = len(b)
d = list(zip(*(e.split(';') for e in b)))
shopname = []
shopaddress = []
shopcity = []
shopphone = []
shopwebsite = []
f = d[0]
g = d[1]
h = d[2]
i = d[3]
j = d[4]
e = -1
for n in range(0, 5):
e = e + 1
sn = f[n]
sn.title()
print(sn)
shopname.append(sn)
sa = g[n]
sa.title()
shopaddress.append(sa)
sc = h[n]
sc.title()
shopcity.append(sc)
Shopstxt.csv is all upper case letters and I want to convert them to title. I thought this would do it but it doesn't...it still leaves them all upper case. What am I doing wrong?
I also want to save the file back. Just wanting to check on a couple of things real quick like as well...time pressed.
When I combine the file back together, before writing it back to the drive do I have to add an '\n' at the end of each line or does it automatically include the '\n' when I write each line to the file?
Strings are immutable, so you need to asign the result of title():
sa = sa.title()
sc = sc.title()
Also, if you do this:
with open("bla.txt", "wt") as outfile:
outfile.write("stuff")
outfile.write("more stuff")
then this will not automatically add line endings.
A quick way to add line endings would be this:
textblobb = "\n".join(list_of_text_lines)
with open("bla.txt", "wt") as outfile:
outfile.write(textblobb)
As long as textblobb isn't inefficiently large and fits into memory, that should do the trick nicely.
Use the .title() method when defining your variables like I did in the code below. As others have mentioned, strings are immutable so save yourself a step and create the string you need in one line.
for n in range(0, 5):
e = e + 1
sn = f[n].title() ### Grab and modify the list index before assigning to your variable
print(sn)
shopname.append(sn)
sa = g[n].title() ###
shopaddress.append(sa)
sc = h[n].title() ###
shopcity.append(sc)

Resources