How should I put these lists together? - python

I am creating an interface with Pyqt. This interface generates a .txt file.
Basically in text_file, I generate the .txt file, where the title of the file is linked to radio_value and the content of the file in clickconector. The latter is where I have my problem. relleno is a list where is what is filled in the form, and form_label the labels of this. I need to join both lists into one, I tried from the most basic: a = zip (form_label, Relleno), but it gives an error: TypeError: can only join an iterable (I know it has to do with the join of a text file), and many others that basically gives the same error and I need to write that correctly (I know that the return of the code is wrong, it will only return the first tuple. I try to repeat that but with all the tuples)
I'll skip the part where I configure buttons to be more specific. Maybe It's a rookie mistake, but I've tried for hours and can't get what I want. Any constructive criticism is welcome
def clickconector (self):
relleno = [self.bitacora.text(), self.turno.text(), self.asesor.text(), self.item.text(), self.modelo.text(), self.identificacion.text(), self.rig.text(),self.horometro.text(),self.condicion.text(),self.orden.text(), self.observacion.text()]
form_label = ["bitacora", 'turno', 'asesor', 'item', 'modelo', 'identificacion', 'rig', 'horometro', 'condicion', 'orden', 'observacion']
for a,b in zip (form_label, relleno):
print (a,b)
def radio_value (self):
if self.pendiente.isChecked():
return 'Pendiente' , self.bitacora.text()
if self.terminado.isChecked():
return 'Terminado', self.bitacora.text()
def text_file(self):
filename = f"{' '.join(self.radio_value())}.txt"
with open(filename, "w") as f:
f.write(" ".join(self.clickconector()))
return f

Your error comes from the line
f.write(" ".join(self.clickconector()))
clickconector() doesn't return anything thus the error says only iterable~.
Try returning some text in iterable or just the text.
----- update -----
I'll change it like this
def clickconector (self):
relleno = [self.bitacora.text(), self.turno.text(), self.asesor.text(), self.item.text(), self.modelo.text(), self.identificacion.text(), self.rig.text(),self.horometro.text(),self.condicion.text(),self.orden.text(), self.observacion.text()]
form_label = ["bitacora", 'turno', 'asesor', 'item', 'modelo', 'identificacion', 'rig', 'horometro', 'condicion', 'orden', 'observacion']
return [str(label_text) for label_text in zip(form_label, relleno)] # just return list of tuples
def radio_value (self):
if self.pendiente.isChecked():
return 'Pendiente' , self.bitacora.text()
if self.terminado.isChecked():
return 'Terminado', self.bitacora.text()
def text_file(self):
filename = f"{' '.join(self.radio_value())}.txt"
with open(filename, "w") as f:
f.write(" ".join(self.clickconector()))
return f

Related

how to append in a specific line in file in python 3?

I am making a program for my computer science assignment. I need to make a quiz for 3 classes in a primary school. Then the results have to be saved in a file. I've done the program till here but the next task asks me to let each student have 3 turns and give an average for each one.
here is the code I used to save the results into a text file:
def savetofile():
result = result ="\n "+ namestudent.get() + " "fscore.get()+"/4"
messagebox.showinfo("results", "your results been saved successfuly")
if int(year.get())==1:
f = open('results C1.txt', 'a')
f.write(result)
f.close()
if int(year.get())==2:
f = open('results C2.txt', 'a')
f.write(result)
f.close()
if int(year.get())==3:
f = open('results C3.txt', 'a')
f.write(result)
f.close()
how can I check if the new user taking the quiz has already done the quiz or not and how can I add the new results of that person in front of their name also how can I take the average of their 3 sets of score.
first of all, you output your results been saved successfully before writing to a file, which is not true, because the file writing can fail and thus not end up done successfully.
Then you do open and close your file in the following way:
f = open('results C1.txt', 'a')
f.write(result)
f.close()
which is wrong as well because if the write fails for any reason (like an exception reading the result variable), the file won't be closed properly, and the output may not get flushed to it.
You should instead do:
with open('results C1.txt', 'a') as f:
f.write(result)
which will take care of opening, flushing and closing the file correctly.
Then the following line is wrong:
result = result ="\n "+ namestudent.get() + " "fscore.get()+"/4"
as it misses a + between the spaces and the score result. You'd better write something like (with the \n at the end to avoid having an empty first line):
result = "{} {}/4\n".format(namestudent.get(), fscore.get())
how can I check if the new user taking the quiz has already done the quiz or not?
To do it you need to do the following algorithm:
# to open the file:
with open('results C1.txt', 'r') as results:
# read the file
for result in results:
# split the line
name, score = result.split(" ")
if name == namestudent.get():
# do something when the student exists
how can I add the new results of that person in front of their name also how can I take the average of their 3 sets of score?
Then my suggestion for this is to not work directly with the files, but instead open up a "cache" dictionary of all the scores:
def load_scores():
years_tuple_default = (None, None, None) # create a tuple with as many entries as there can be years
student_scores = dict()
with open('results C1.txt', 'r') as results:
# read the file
for result in results:
# split the line
name, score = result.split(" ")
student_scores.setdefault(name, years_tuple_default)[0] = score
with open('results C2.txt', 'r') as results:
# read the file
for result in results:
# split the line
name, score = result.split(" ")
student_scores.setdefault(name, years_tuple_default)[1] = score
with open('results C3.txt', 'r') as results:
# read the file
for result in results:
# split the line
name, score = result.split(" ")
student_scores.setdefault(name, years_tuple_default)[2] = score
return student_scores
Then, create a function save the cache into the files:
def save_scores(student_scores):
with open('results C1.txt', 'w') as results_y1:
with open('results C2.txt', 'w') as results_y2:
with open('results C3.txt', 'w') as results_y3:
for student, scores in student_scores:
results_y1.write("{} {}/4\n".format(student, scores[0]))
results_y2.write("{} {}/4\n".format(student, scores[1]))
results_y3.write("{} {}/4\n".format(student, scores[2]))
print("Results saved!")
And finally, work against that cache:
def update_scores(student_scores, namestudent, fscore, year):
if namestudent.get() not in student_scores.keys():
# add the student entry
student_scores.setdefault(namestudent.get(), (None, None, None))[int(year.get())-1] = fscore.get()
else:
# update the student year entry
student_scores[int(year.get())-1] = fscore.get()
which would end up with a code looking like that to put it all together:
student_scores = load_scores()
# do your stuff to get the namestudent/fscore/year data
update_scores(student_scores, namestudent, fscore, year)
# when all updates are done, you can alter the files
save_scores(student_scores)
To sum up:
split your algorithm into functions,
protect your file handling using the with statement
read the files once to create a "cache" to work against, and save the result at once when you're done
Bonus ideas
use a class
From there, to get further, you could create a class, and have all that as methods:
class StudentScores:
def __init__(self):
self._student_scores = dict()
def load_scores(self):
# TODO
def save_scores(self):
# TODO
def update_score(self):
# TODO
And then calculating the mean of the scores is simple stupid, it's just adding a method to the class which prototype would look like:
class StudentScores:
…
def get_mean_scores(self, student):
score_y1, score_y2, score_y3 = self._student_scores[student]
score = # calculate the average of the three score
return score
use a with statement
And if you want to shine, you could implement it as working with the with statement:
class StudentScores:
def __init__(self):
self._student_scores = dict()
def load_scores(self):
# TODO
def save_scores(self):
# TODO
def update_score(self):
# TODO
def __enter__(self):
self.load_scores()
return self
def __exit__(self, type, value, traceback):
self.save_scores()
And then your code would like that:
with StudentScores() as student_scores:
# do your stuff to get the namestudent/fscore/year data
update_scores(student_scores, namestudent, fscore, year)
To conclude, I'm showing you how I'd design something to do your assignment, whereas I'm not trying to do your assignment on your behalf. My goal here is to help you get ideas and learn something about software design and python. Some things may be above your current level (like how to create a with statement), but as you're learning, you'll be able to fully understand and apply everything I'm telling you here.
HTH

Write to file python - Linebreak issue (\n)

I have a problem where the function just overwrites the line thats already there in a .txt file. The function is supposed to write a highscore to a file when the game quits (I have made a snake game by following a youtube tutorial). I can't quite figure out why it won't start on a new line, can anyone please explain the logic behind it, and how I fix it? I read somewhere that instead of "w" in f.open(), I should type "rb" or something. Since I'm kinda new to this "writing-to-file" thingy, I find it difficult.
Also, I want to sort the highscores from highest to lowest in the file (in other words, sort finalScore from highest to lowest). I have no idea how I should go on and code that, so I'd appreicate some help. You see, I want to print out the current highscores to the console (in order to make a scoreboad)
Heres the code:
import random
import time
name = "Andreas"
finalScore = random.randint(1,10)
def scoreToFile(finalScore):
#Has to be generated here, since we need the exact current time
currentTime = time.strftime("%c")
print("Sucsessfully logged score (finalScore) to highscores.txt")
f = open("highscores.txt", "w")
#fileOutput = [(currentTime, ":", name, "-", finalScore)]
fileOutput = [(finalScore, "-", name, currentTime)]
for t in fileOutput:
line = ' '.join(str(x) for x in t)
f.write(line + "\n")
f.close()
scoreToFile(finalScore)
Anyways, merry christmas my fellow python geeks! :D
1) one option is to open the file in append mode.
replace:
f = open("highscores.txt", "w")
with:
f = open("highscores.txt", "a")
2) another option is to replace this block,
f = open("highscores.txt", "w")
#fileOutput = [(currentTime, ":", name, "-", finalScore)]
fileOutput = [(finalScore, "-", name, currentTime)]
for t in fileOutput:
line = ' '.join(str(x) for x in t)
myfile.write(line + "\n")
f.close()
and use a with style
with open("highscores.txt", "a") as myfile:
#fileOutput = [(currentTime, ":", name, "-", finalScore)]
fileOutput = [(finalScore, "-", name, currentTime)]
for t in fileOutput:
line = ' '.join(str(x) for x in t)
myfile.write(line + "\n")
I prefer the second style as it is more safe and clean.
Mode w overwrites an existing file; mode 'a' appends to it. Also, the best way to handle a file is usually with the with statement, which ensures the closing on your behalf; so:
fileOutput = [(finalScore, "-", name, currentTime)]
with open("highscores.txt", "a") as f:
for t in fileOutput:
line = ' '.join(str(x) for x in t)
f.write(line + "\n")
For sorting, you need be able to extract the final score as a number from a line:
def minus_score(line):
return -int(line.split()[0])
then the total work will be done as:
def sorted_by_score():
with open("highscores.txt", "r") as f:
result = list(f)
return sorted(result, key=minus_score)
This will give you a list lines sorted in ascending order of score (the latter's the reason score is negating the number, though one might also choose to have it just return the number and reverse the sorting), for you to loop on and further process.
Added: so on the OP's request here's how the whole program might be (assuming the existence of a function that either plays a game and returns player name and final score, or else returns None when no more games are to be played and the program must exit).
import time
def play_game():
""" play a game and return name, finalscore;
return None to mean no more games, program finished.
THIS function you'd better code yourself!-)
"""
def scoreToFile(name, finalScore):
""" Add a name and score to the high-scores file. """
currentTime = time.strftime("%c")
fileOutput = finalScore, "-", name, currentTime
line = ' '.join(str(x) for x in fileOutput)
with open("highscores.txt", "a") as f:
f.write(line + "\n")
def minus_score(line):
""" just for sorting purposes, not called directly. """
return -int(line.split()[0])
def sorted_by_score():
""" return list of score lines sorted in descending order of score. """
with open("highscores.txt", "r") as f:
return sorted(f, key=minus_score)
def main():
while True:
game_result = play_game()
if game_result is None: break
scoreToFile(*game_result)
for line in sorted_by_score:
print(line.strip())
As others have mentioned, the problem is you're not opening the file in append mode, so it overwrites it every time rather than adding to it.
However, if you also want to keep the data in the file sorted, you do want to overwrite it each time, since the order of its contents will likely have been changed with the addition. To do that requires first reading it contents in, updating the data, and then writing it back out.
Here's a modified version of your function that does that. I also changed how the data in file is stored to what is known as Comma (or Character) Separated Values (CSV) format, because Python includes acsvmodule which makes it very easy to read, write, and do other things with such files.
import csv
import random
import time
highscores_filename = "highscores.txt"
HighScoresFirst = True # Determines sort order of data in file
def scoreToFile(name, finalScore):
currentTime = time.strftime("%c")
# Try reading scores from existing file.
try:
with open(highscores_filename, "r", newline='') as csvfile:
highscores = [row for row in csv.reader(csvfile, delimiter='-')]
except FileNotFoundError:
highscores = []
# Add this score to the end of the list.
highscores.append([str(finalScore), name, currentTime])
# Sort updated list by numeric score.
highscores.sort(key=lambda item: int(item[0]), reverse=HighScoresFirst)
# Create/rewrite highscores file from highscores list.
with open(highscores_filename, "w", newline='') as csvfile:
writer = csv.writer(csvfile, delimiter='-')
writer.writerows(highscores)
print("successfully logged score (finalScore) to highscores.txt")
# Simulate using the function several times.
name = "Name"
for i in range(1, 4):
finalScore = random.randint(1,10)
scoreToFile(name + str(i), finalScore)
time.sleep(random.randint(1,3)) # Pause so time values will vary.

Python class - Set & Delete methods?

First I'd like to mention that I am completely new to Python and I've found it a bit difficult to transition from C++. I apologize if my question comes off as elementary.
I have a class for 'songs' which I have initialized as following. It takes in data from a file that contains a song's ID, name, genre etc. all separated by ::.
def __init__(self):
self.song_names = dict()
self.song_genres = dict()
def load_songs(self, song_id):
f = open(song_id)
for line in f:
line = line.rstrip()
component = line.split("::")
sid = components[0]
same= components[1]
sgenre=components[2]
self.song_names[mid] = sname
self.song_genres[mid] = sgenre
f.close()
The program also takes in data from a file with 'users' information, separated as
UserID::Gender::Age::Occupation::Zip etc. and a file with 'ratings'.
I would I implement a function like def set_song(sid, list((title,genres)))
and something like delete_song(sid) ?
I'm going to have to wind up doing a ton more other functions, but if someone could help me with those two - at least to have a better idea of structure and syntax - handling the others should be easier.
Why not just inherit from dict and use its interface? That way you can use Python's standard mapping operations instead of rolling your own:
class Songs(dict):
def load(self, song_id):
with open(song_id, 'r') as f:
for line in f:
sid, name, genre = line.rstrip().split('::')[:3]
self[sid] = [name, genre]
mysongs = Songs()
mysongs.load('barnes_and_barnes__fish_heads')
mysongs['barnes_and_barnes__fish_heads'] = ['roly poly', 'strange'] # set
del mysongs['barnes_and_barnes__fish_heads'] # delete

How to read a CSV file in reverse order in Python?

I know how to do it for a TXT file, but now I am having some trouble doing it for a CSV file.
How can I read a CSV file from the bottom in Python?
Pretty much the same way as for a text file: read the whole thing into a list and then go backwards:
import csv
with open('test.csv', 'r') as textfile:
for row in reversed(list(csv.reader(textfile))):
print ', '.join(row)
If you want to get fancy, you could write a lot of code that reads blocks starting at the end of the file and working backwards, emitting a line at a time, and then feed that to csv.reader, but that will only work with a file that can be seeked, i.e. disk files but not standard input.
Some of us have files that do not fit into memory, could anyone come with a solution that does not require storing the entire file in memory?
That's a bit trickier. Luckily, all csv.reader expects is an iterator-like object that returns a string (line) per call to next(). So we grab the technique Darius Bacon presented in "Most efficient way to search the last x lines of a file in python" to read the lines of a file backwards, without having to pull in the whole file:
import os
def reversed_lines(file):
"Generate the lines of file in reverse order."
part = ''
for block in reversed_blocks(file):
for c in reversed(block):
if c == '\n' and part:
yield part[::-1]
part = ''
part += c
if part: yield part[::-1]
def reversed_blocks(file, blocksize=4096):
"Generate blocks of file's contents in reverse order."
file.seek(0, os.SEEK_END)
here = file.tell()
while 0 < here:
delta = min(blocksize, here)
here -= delta
file.seek(here, os.SEEK_SET)
yield file.read(delta)
and feed reversed_lines into the code to reverse the lines before they get to csv.reader, removing the need for reversed and list:
import csv
with open('test.csv', 'r') as textfile:
for row in csv.reader(reversed_lines(textfile)):
print ', '.join(row)
There is a more Pythonic solution possible, which doesn't require a character-by-character reversal of the block in memory (hint: just get a list of indices where there are line ends in the block, reverse it, and use it to slice the block), and uses chain out of itertools to glue the line clusters from successive blocks together, but that's left as an exercise for the reader.
It's worth noting that the reversed_lines() idiom above only works if the columns in the CSV file don't contain newlines.
Aargh! There's always something. Luckily, it's not too bad to fix this:
def reversed_lines(file):
"Generate the lines of file in reverse order."
part = ''
quoting = False
for block in reversed_blocks(file):
for c in reversed(block):
if c == '"':
quoting = not quoting
elif c == '\n' and part and not quoting:
yield part[::-1]
part = ''
part += c
if part: yield part[::-1]
Of course, you'll need to change the quote character if your CSV dialect doesn't use ".
Building on #mike-desimone 's answer. Here's a solution that provides the same structure as a python file object but is read in reverse, line by line:
import os
class ReversedFile(object):
def __init__(self, f, mode='r'):
"""
Wraps a file object with methods that make it be read in reverse line-by-line
if ``f`` is a filename opens a new file object
"""
if mode != 'r':
raise ValueError("ReversedFile only supports read mode (mode='r')")
if not type(f) == file:
# likely a filename
f = open(f)
self.file = f
self.lines = self._reversed_lines()
def _reversed_lines(self):
"Generate the lines of file in reverse order."
part = ''
for block in self._reversed_blocks():
for c in reversed(block):
if c == '\n' and part:
yield part[::-1]
part = ''
part += c
if part: yield part[::-1]
def _reversed_blocks(self, blocksize=4096):
"Generate blocks of file's contents in reverse order."
file = self.file
file.seek(0, os.SEEK_END)
here = file.tell()
while 0 < here:
delta = min(blocksize, here)
here -= delta
file.seek(here, os.SEEK_SET)
yield file.read(delta)
def __getattribute__(self, name):
"""
Allows for the underlying file attributes to come through
"""
try:
# ReversedFile attribute
return super(ReversedFile, self).__getattribute__(name)
except AttributeError:
# self.file attribute
return getattr(self.file, name)
def __iter__(self):
"""
Creates iterator
"""
return self
def seek(self):
raise NotImplementedError('ReversedFile does not support seek')
def next(self):
"""
Next item in the sequence
"""
return self.lines.next()
def read(self):
"""
Returns the entire contents of the file reversed line by line
"""
contents = ''
for line in self:
contents += line
return contents
def readline(self):
"""
Returns the next line from the bottom
"""
return self.next()
def readlines(self):
"""
Returns all remaining lines from the bottom of the file in reverse
"""
return [x for x in self]
Go for it. This is simple program to reverse the rows from a CSV file.
import csv
BC_file = open('Master.csv', 'rb')
BC_reader = csv.reader(BC_file)
next(BC_reader)
for row in reversed(list(BC_reader)):
print row[0]

Python script how can it do short

I have written a script on a python "icecast server", and I changed some strings in "/etc/icecast2/icecast.xml" like this:
import os,sys,re
def ices2():
changedir=open(pathh + "icecast3.xml", "w")
data=open("/etc/icecast2/icecast.xml").read()
changedir.write(re.sub("<source-password>hackme</source-password>","<source-password>123</source-password>" % x,data))
changedir.close()
ices2()
def ices1():
changedir1=open(pathh + "icecast2.xml", "w")
data=open(pathh + "icecast3.xml").read()
changedir1.write(re.sub("<relay-password>hackme</relay-password>", "<relay-password>123</relay-password>" % x,data))
changedir1.close()
os.remove(pathh + "icecast3.xml")
ices1()
def ices():
changedir2=open("/etc/icecast2/icecast.xml", "w")
data=open(pathh + "icecast2.xml").read()
changedir2.write(re.sub("<admin-password>hackme</admin-password>","<admin-password>123</admin-password>" % x,data))
changedir2.close()
os.remove(pathh + "icecast2.xml")
ices()
...but it's too long for the script. How can I shorten it? I need to do some changes in one file, open it to make changes and close it without any lost data. I know that it can be done in one function, but how to do it I don't know.
I need three changes in one function like this:
def ices():
changedir=open(pathh + "icecast3.xml", "w")
data=open("/etc/icecast2/icecast.xml").read()
changedir.write(re.sub("<source-password>hackme</source-password>","<source-password>123</source-password>",data))
changedir1.write(re.sub("<relay-password>hackme</relay-password>", "<relay-password>123</relay-password>",data))
changedir2.write(re.sub("<admin-password>hackme</admin-password>","<admin-password>123</admin-password>",data))
changedir.close()
i did it in one function and my script short than upper one. But it's wrong i need do it correctly
changedir=open(pathh + "icecast3.xml", "w")
data=open("/etc/icecast2/icecast.xml").read()
Here I create a new file "pathh + "icecast3.xml" (pathh-/home/user/Downloads), but I need to open file:
"/etc/icecast2/icecast.xml"
...read it and write changes to the same file.
All three functions do the same so you can join them into one. This is not complete solution but I think that you could go on from here on your own:
import os,sys,re
def ices(in_path, out_path, remove=False):
changedir = open(out_path, "w")
data = open(in_path, 'r')
changedir.write(re.sub("<source-password>hackme</source-password>","<source-password>123</source-password>" % x,data.read())) # this is wrong as well but I take it as an example
changedir.close()
data.close()
if remove:
os.remove(in_path)
You can call this function with:
ices(base_path + 'icecast2.xml', base_path + 'icecast3.xml', True)
Hints:
it's better to use os.path.join for creating the full paths (as opposed to string concatenation)
look at with statement and cosider using it for increased readability
EDIT (respecting the clarification in comment):
Sorry I missed the different strings in write. You can do it simply like this:
f = open(filename, 'r')
data = f.read()
f.close()
for tag in ['source', 'relay', 'admin']
sub_str = "<{tag_name}>%s</{tag_name}>".format(tag_name=tag+'-password')
data = re.sub(sub_str % 'hackme', sub_str % '123', data)
f = open(filename+'.new', 'w')
f.write(data)
f.close()

Categories

Resources