Why while loop is sticking at raw_input? (python) - python

In the following code i am trying to make a "more" command (unix) using python script by reading the file into a list and printing 10 lines at a time and then asking user do you want to print next 10 lines (Print More..).
Problem is that raw_input is asking again and again input if i give 'y' or 'Y' as input and do not continue with the while loop and if i give any other input the while loop brakes.
My code may not be best as am learning python.
import sys
import string
lines = open('/Users/abc/testfile.txt').readlines()
chunk = 10
start = 0
while 1:
block = lines[start:chunk]
for i in block:
print i
if raw_input('Print More..') not in ['y', 'Y']:
break
start = start + chunk
Output i am getting for this code is:-
--
10 lines from file
Print More..y
Print More..y
Print More..y
Print More..a

You're constructing your slices wrong: The second parameter in a slice gives the stop position, not the chunk size:
chunk = 10
start = 0
stop = chunk
end = len(lines)
while True:
block = lines[start:stop] # use stop, not chunk!
for i in block:
print i
if raw_input('Print More..') not in ['y', 'Y'] or stop >= end:
break
start += chunk
stop += chunk

Instead of explaining why your code doesn't work and how to fix it (because Tim Pietzcker already did an admirable job of that), I'm going to explain how to write code so that issues like this don't come up in the first place.
Trying to write your own explicit loops, checks, and index variables is difficult and error-prone. That's why Python gives you nice tools that almost always make it unnecessary to do so. And that's why you're using Python instead of C.
For example, look at the following version of your program:
count = 10
with open('/Users/abc/testfile.txt', 'r') as testfile:
for i, line in enumerate(testfile):
print line
if (i + 1) % count == 0:
if raw_input('Print More..') not in ['y', 'Y']:
break
This is shorter than the original code, and it's also much more efficient (no need to read the whole file in and then build a huge list in advance), but those aren't very good reasons to use it.
One good reason is that it's much more robust. There's very little explicit loop logic here to get wrong. You don't even need to remember how slices work (sure, it's easy to learn that they're [start:stop] rather than [start:length]… but if you program in another language much more frequently than Python, and you're always writing s.sub(start, length), you're going to forget…). It also automatically takes care of ending when you get to the end of the file instead of continuing forever, closing the file for you (even on exceptions, which is painful to get right manually), and other stuff that you haven't written yet.
The other good reason is that it's much easier to read, because, as much as possible, the code tells you what it's doing, rather than the details of how it's doing it.
But it's still not perfect, because there's still one thing you could easily get wrong: that (i + 1) % count == 0 bit. In fact, I got it wrong in my first attempt (I forgot the +1, so it gave me a "More" prompt after lines 0, 10, 20, … instead of 9, 19, 29, …). If you have a grouper function, you can rewrite it even more simply and robustly:
with open('/Users/abc/testfile.txt', 'r') as testfile:
for group in grouper(testfile, 10):
for line in group:
print line
if raw_input('Print More..') not in ['y', 'Y']:
break
Or, even better:
with open('/Users/abc/testfile.txt', 'r') as testfile:
for group in grouper(testfile, 10):
print '\n'.join(group)
if raw_input('Print More..') not in ['y', 'Y']:
break
Unfortunately, there's no such grouper function built into, say, the itertools module, but you can write one very easily:
def grouper(iterator, size):
return itertools.izip(*[iterator]*size)
(If efficiency matters, search around this site—there are a few questions where people do in-depth comparisons of different ways to achieve the same effect. But usually it doesn't matter. For that matter, if you want to understand why this groups things, search this site, because it's been explained at least twice.)

As #Tim Pietzcker pointed out, there's no need of updating chunk here, just use start+10 instead of chunk.
block = lines[start:start+10]
and update start using start += 10.
Another alternative solution using itertools.islice():
with open("data1.txt") as f:
slc=islice(f,5) #replace 5 by 10 in your case
for x in slc:
print x.strip()
while raw_input("wanna see more : ") in("y","Y"):
slc=islice(f,5) #replace 5 by 10 in your case
for x in slc:
print x.strip()
this outputs:
1
2
3
4
5
wanna see more : y
6
7
8
9
10
wanna see more : n

Related

Is there a proper variable to track how many times a loop has looped?

I'm pretty new to coding, so forgive me if this is super obvious.
When running a while loop, and I want to only run if for a certain amount of times. Ex (python):
question_var = 0
while True:
if condition:
question_var += 1
continue
print("Condition not met")
I want to know if there is a proper variable to use in place of question_var. Similar to how i is used to represent index in a for loop. I understand that this is not necessary, but I just want to learn how to do it right. I have been using a variable named rev
Here is the actual program I am trying to run:
rev = 0
while rev <= 10:
new_file_name = "C# is bad ({}).txt".format(rev)
with open(new_file_name, 'w') as tempfile:
tempfile.write("C# is bad.\nPython is good.\n")
rev += 1
its a gag I made for my friend who likes C#, but when I was making it I felt the the variable rev seemed unprofesional.
using a while True loop is useful in some cases, but it is often not the most idiomatic use in python. As mentioned in the comments, you might find a for loop more appropriate:
for rev in range(0, 11):
new_file_name = "C# is bad ({}).txt".format(rev)
with open(new_file_name, 'w') as tempfile:
tempfile.write("C# is bad.\nPython is good.\n")
Also, you don't really need to use format() here, you could change the second line to:
new_file_name = f"C# is bad ({rev}).txt"
The code follows should be a working approach if you're using Python 3.8, but I can't test it currently since I'm on my phone
counter = 0
maxLoops = 10
while (counter := counter++) < maxLoops:
# do some magic
But a better approach is to use a for loop if you want to loop a certain amount.
maxLoops = 10
for i in range(maxLoops):
# do some magic
But there is no convention for how to run a while loop for a certain amount.
For the first part of the question you could use for and break:
need=2
for i in range(0,11):
if i%3 == 0:
print(i)
need -= 1
if need <= 0:
break
print("i is {} after the loop".format(i))
Output:
0
3
i is 3 after the loop
This loop will run at most 11 times, but finishes immediately when it finds the two special values it is looking for. Also, the loop variable remains accessible after the loop. Yeah, Python is different from C# here.
However, and this leads to the second part of the question, not knowing this means that you may rather want to implement something like this:
for rev in range(0, 11):
new_file_name = "Python is new to me ({}).txt".format(rev)
with open(new_file_name, 'w') as tempfile:
tempfile.write("I will learn Python first.\nAnd even then I will not speak nonsense.\n")

AIO Castle Cavalry - My code is too slow, is there a way I can shorten this?

So I am currently preparing for a competition (Australian Informatics Olympiad) and in the training hub, there is a problem in AIO 2018 intermediate called Castle Cavalry. I finished it:
input = open("cavalryin.txt").read()
output = open("cavalryout.txt", "w")
squad = input.split()
total = squad[0]
squad.remove(squad[0])
squad_sizes = squad.copy()
squad_sizes = list(set(squad))
yn = []
for i in range(len(squad_sizes)):
n = squad.count(squad_sizes[i])
if int(squad_sizes[i]) == 1 and int(n) == int(total):
yn.append(1)
elif int(n) == int(squad_sizes[i]):
yn.append(1)
elif int(n) != int(squad_sizes[i]):
yn.append(2)
ynn = list(set(yn))
if len(ynn) == 1 and int(ynn[0]) == 1:
output.write("YES")
else:
output.write("NO")
output.close()
I submitted this code and I didn't pass because it was too slow, at 1.952secs. The time limit is 1.000 secs. I wasn't sure how I would shorten this, as to me it looks fine. PLEASE keep in mind I am still learning, and I am only an amateur. I started coding only this year, so if the answer is quite obvious, sorry for wasting your time 😅.
Thank you for helping me out!
One performance issue is calling int() over and over on the same entity, or on things that are already int:
if int(squad_sizes[i]) == 1 and int(n) == int(total):
elif int(n) == int(squad_sizes[i]):
elif int(n) != int(squad_sizes[i]):
if len(ynn) == 1 and int(ynn[0]) == 1:
But the real problem is your code doesn't work. And making it faster won't change that. Consider the input:
4
2
2
2
2
Your code will output "NO" (with missing newline) despite it being a valid configuration. This is due to your collapsing the squad sizes using set() early in your code. You've thrown away vital information and are only really testing a subset of the data. For comparison, here's my complete rewrite that I believe handles the input correctly:
with open("cavalryin.txt") as input_file:
string = input_file.read()
total, *squad_sizes = map(int, string.split())
success = True
while squad_sizes:
squad_size = squad_sizes.pop()
for _ in range(1, squad_size):
try:
squad_sizes.remove(squad_size) # eliminate n - 1 others like me
except ValueError:
success = False
break
else: # no break
continue
break
with open("cavalryout.txt", "w") as output_file:
print("YES" if success else "NO", file=output_file)
Note that I convert all the input to int early on so I don't have to consider that issue again. I don't know whether this will meet AIO's timing constraints.
I can see some things in there that might be inefficient, but the best way to optimize code is to profile it: run it with a profiler and sample data.
You can easily waste time trying to speed up parts that don't need it without having much effect. Read up on the cProfile module in the standard library to see how to do this and interpret the output. A profiling tutorial is probably too long to reproduce here.
My suggestions, without profiling,
squad.remove(squad[0])
Removing the start of a big list is slow, because the rest of the list has to be copied as it is shifted down. (Removing the end of the list is faster, because lists are typically backed by arrays that are overallocated (more slots than elements) anyway, to make .append()s fast, so it only has to decrease the length and can keep the same array.
It would be better to set this to a dummy value and remove it when you convert it to a set (sets are backed by hash tables, so removals are fast), e.g.
dummy = object()
squad[0] = dummy # len() didn't change. No shifting required.
...
squad_sizes = set(squad)
squad_sizes.remove(dummy) # Fast lookup by hash code.
Since we know these will all be strings, you can just use None instead of a dummy object, but the above technique works even when your list might contain Nones.
squad_sizes = squad.copy()
This line isn't required; it's just doing extra work. The set() already makes a shallow copy.
n = squad.count(squad_sizes[i])
This line might be the real bottleneck. It's effectively a loop inside a loop, so it basically has to scan the whole list for each outer loop. Consider using collections.Counter for this task instead. You generate the count table once outside the loop, and then just look up the numbers for each string.
You can also avoid generating the set altogether if you do this. Just use the Counter object's keys for your set.
Another point unrelated to performance. It's unpythonic to use indexes like [i] when you don't need them. A for loop can get elements from an iterable and assign them to variables in one step:
from collections import Counter
...
count_table = Counter(squad)
for squad_size, n in count_table.items():
...
You can collect all occurences of the preferred number for each knight in a dictionary.
Then test if the number of knights with a given preferred number is divisible by that number.
with open('cavalryin.txt', 'r') as f:
lines = f.readlines()
# convert to int
list_int = [int(a) for a in lines]
#initialise counting dictionary: key: preferred number, item: empty list to collect all knights with preferred number.
collect_dict = {a:[] for a in range(1,1+max(list_int[1:]))}
print(collect_dict)
# loop though list, ignoring first entry.
for a in list_int[1:]:
collect_dict[a].append(a)
# initialise output
out='YES'
for key, item in collect_dict.items():
# check number of items with preference for number is divisilbe
# by that number
if item: # if list has entries:
if (len(item) % key) > 0:
out='NO'
break
with open('cavalryout.txt', 'w') as f:
f.write(out)

Doesn't process the loop

I am trying to learn python and trying to create a simple application where I have it set to read lines from the text file. The first line is the question and second line is answer. Now, I am able to read the question and answer. But the part where I compare user answer with the actual answer, it doesn't perform the actions in the loop even when the answer entered is correct.
My code :
def populate():
print("**************************************************************************************************************")
f=open("d:\\q.txt")
questionList=[]
b = 1
score=0
start=0
for line in f.read().split("\n"):
questionList.append(line)
while b<len(questionList):
a = questionList[start]
print(a)
userinput=input("input user")
answer=questionList[b]
b = b + 2
print(answer)
if userinput==answer:
score =score+1
print(score)
else:
break
start += 2
I would really appreciate any guidance on this.
My q.txt file:
1. Set of instructions that you write to tell a computer what to do.
Program
2. A language's set of rules.
Syntax
3. Describes the programs that operate the computer.
System Software
4.To achieve a working program that accomplishes its intended tasks by removing all syntax and logical errors from the program
Debugging
5.a program that creates and names computer memory locations and can hold values, and write a series of steps or operations to manipulate those values
Procedural Program
6. The named computer memory locations.
Variables
7. The style of typing the first initial of an identifier in lowercase and making the initial of the second word uppercase. -- example -- "payRate"
Camel Casing
8. Individual operations within a computer program that are often grouped together into logical units.
Methods
9. This is an extension of procedural programming in terms of using variables and methods, but it focuses more on objects.
Object Oriented Programming
10. A concrete entity that has behaviors and attributes.
Objects
Your code was:
always asking the same question: questionList[start]
throwing away the value of every
replacing every space in answers with nothing, so "System Software" becomes "SystemSoftware"
failing to factor in case: need to use .lower() on userinput and answer.
Here's a more pythonic implementation:
#!/usr/bin/env python3
from itertools import islice
# Read in every line and then put alternating lines together in
# a list like this [ (q, a), (q, a), ... ]
def get_questions(filename):
with open(filename) as f:
lines = [line.strip() for line in f]
number_of_lines = len(lines)
if number_of_lines == 0:
raise ValueError('No questions in {}'.format(filename))
if number_of_lines % 2 != 0:
raise ValueError('Missing answer for last question in {}'.filename)
return list(zip(islice(lines, 0, None, 2), islice(lines, 1, None, 2)))
def quiz(questions):
score = 0
for question, answer in questions:
user_answer = input('\n{}\n> '.format(question))
if user_answer.lower() == answer.lower():
print('Correct')
score += 1
else:
print('Incorrect: {}'.format(answer))
return score
questions = get_questions('questions.txt')
score = quiz(questions)
num_questions = len(questions)
print('You scored {}/{}'.format(score, num_questions))

Why re is not compiling 'if' when there is 'else'?

Hello I'm facing a problem and I don't how to fix it. All I know is that when I add an else statement to my if statement the python execution always goes to the else statement even there is there a true statement in if and can enter the if statement.
Here is the script, without the else statement:
import re
f = open('C:\Users\Ziad\Desktop\Combination\MikrofullCombMaj.txt', 'r')
d = open('C:\Users\Ziad\Desktop\Combination\WhatsappResult.txt', 'r')
w = open('C:\Users\Ziad\Desktop\Combination\combination.txt','w')
s=""
av =0
b=""
filtred=[]
Mlines=f.readlines()
Wlines=d.readlines()
for line in Wlines:
Wspl=line.split()
for line2 in Mlines:
Mspl=line2.replace('\n','').split("\t")
if ((Mspl[0]).lower()==(Wspl[0])):
Wspl.append(Mspl[1])
if(len(Mspl)>=3):
Wspl.append(Mspl[2])
s="\t".join(Wspl)+"\n"
if s not in filtred:
filtred.append(s)
break
for x in filtred:
w.write(x)
f.close()
d.close()
w.close()
with the else statement and I want else for the if ((Mspl[0]).lower()==(Wspl[0])):
import re
f = open('C:\Users\Ziad\Desktop\Combination\MikrofullCombMaj.txt', 'r')
d = open('C:\Users\Ziad\Desktop\Combination\WhatsappResult.txt', 'r')
w = open('C:\Users\Ziad\Desktop\Combination\combination.txt','w')
s=""
av =0
b=""
filtred=[]
Mlines=f.readlines()
Wlines=d.readlines()
for line in Wlines:
Wspl=line.split()
for line2 in Mlines:
Mspl=line2.replace('\n','').split("\t")
if ((Mspl[0]).lower()==(Wspl[0])):
Wspl.append(Mspl[1])
if(len(Mspl)>=3):
Wspl.append(Mspl[2])
s="\t".join(Wspl)+"\n"
if s not in filtred:
filtred.append(s)
break
else:
b="\t".join(Wspl)+"\n"
if b not in filtred:
filtred.append(b)
break
for x in filtred:
w.write(x)
f.close()
d.close()
w.close()
first of all, you're not using "re" at all in your code besides importing it (maybe in some later part?) so the title is a bit misleading.
secondly, you are doing a lot of work for what is basically a filtering operation on two files. Remember, simple is better than complex, so for starters, you want to clean your code a bit:
you should use a little more indicative names than 'd' or 'w'. This goes for 'Wsplt', 's' and 'av' as well. Those names don't mean anything and are hard to understand (why is the d.readlines named Wlines when ther's another file named 'w'? It's really confusing).
If you choose to use single letters, it should still make sense (if you iterate over a list named 'results' it makes sense to use 'r'. 'line1' and 'line2' however, are not recommanded for anything)
You don't need parenthesis for conditions
You want to use as little variables as you can as to not get confused. There's too much different variables in your code, it's easy to get lost. You don't even use some of them.
you want to use strip rather than replace, and you want the whole 'cleaning' process to come first and then just have a code the deals with the filtering logic on the two lists. If you split each line according to some logic, and you don't use the original line anywhere in the iteration, then you can do the whole thing in the beggining.
Now, I'm really confused what you're trying to achieve here, and while I don't understand why your doing it that way, I can say that looking at your logic you are repeating yourself a lot. The action of checking against the filtered list should only happend once, and since it happens regardless of whether the 'if' checks out or not, I see absolutely no reason to use an 'else' clause at all.
Cleaning up like I mentioned, and re-building the logic, the script looks something like this:
# PART I - read and analyze the lines
Wappresults = open('C:\Users\Ziad\Desktop\Combination\WhatsappResult.txt', 'r')
Mikrofull = open('C:\Users\Ziad\Desktop\Combination\MikrofullCombMaj.txt', 'r')
Wapp = map(lambda x: x.strip().split(), Wappresults.readlines())
Mikro = map(lambda x: x.strip().split('\t'), Mikrofull.readlines())
Wappresults.close()
Mikrofull.close()
# PART II - filter using some logic
filtred = []
for w in Wapp:
res = w[:] # So as to copy the list instead of point to it
for m in Mikro:
if m[0].lower() == w[0]:
res.append(m[1])
if len(m) >= 3 :
res.append(m[2])
string = '\t'.join(res)+'\n' # this happens regardles of whether the 'if' statement changed 'res' or not
if string not in filtred:
filtred.append(string)
# PART III - write the filtered results into a file
combination = open('C:\Users\Ziad\Desktop\Combination\combination.txt','w')
for comb in filtred:
combination.write(comb)
combination.close()
I can't promise it will work (because again, like I said, I don't know what you're trying to achive) but this should be a lot easier to work with.

What is the Pythonic way to implement a simple FSM?

Yesterday I had to parse a very simple binary data file - the rule is, look for two bytes in a row that are both 0xAA, then the next byte will be a length byte, then skip 9 bytes and output the given amount of data from there. Repeat to the end of the file.
My solution did work, and was very quick to put together (even though I am a C programmer at heart, I still think it was quicker for me to write this in Python than it would have been in C) - BUT, it is clearly not at all Pythonic and it reads like a C program (and not a very good one at that!)
What would be a better / more Pythonic approach to this? Is a simple FSM like this even still the right choice in Python?
My solution:
#! /usr/bin/python
import sys
f = open(sys.argv[1], "rb")
state = 0
if f:
for byte in f.read():
a = ord(byte)
if state == 0:
if a == 0xAA:
state = 1
elif state == 1:
if a == 0xAA:
state = 2
else:
state = 0
elif state == 2:
count = a;
skip = 9
state = 3
elif state == 3:
skip = skip -1
if skip == 0:
state = 4
elif state == 4:
print "%02x" %a
count = count -1
if count == 0:
state = 0
print "\r\n"
The coolest way I've seen to implement FSMs in Python has to be via generators and coroutines. See this Charming Python post for an example. Eli Bendersky also has an excellent treatment of the subject.
If coroutines aren't familiar territory, David Beazley's A Curious Course on Coroutines and Concurrency is a stellar introduction.
You could give your states constant names instead of using 0, 1, 2, etc. for improved readability.
You could use a dictionary to map (current_state, input) -> (next_state), but that doesn't really let you do any additional processing during the transitions. Unless you include some "transition function" too to do extra processing.
Or you could do a non-FSM approach. I think this will work as long as 0xAA 0xAA only appears when it indicates a "start" (doesn't appear in data).
with open(sys.argv[1], 'rb') as f:
contents = f.read()
for chunk in contents.split('\xaa\xaa')[1:]:
length = ord(chunk[0])
data = chunk[10:10+length]
print data
If it does appear in data, you can instead use string.find('\xaa\xaa', start) to scan through the string, setting the start argument to begin looking where the last data block ended. Repeat until it returns -1.
I am a little apprehensive about telling anyone what's Pythonic, but here goes. First, keep in mind that in python functions are just objects. Transitions can be defined with a dictionary that has the (input, current_state) as the key and the tuple (next_state, action) as the value. Action is just a function that does whatever is necessary to transition from the current state to the next state.
There's a nice looking example of doing this at http://code.activestate.com/recipes/146262-finite-state-machine-fsm. I haven't used it, but from a quick read it seems like it covers everything.
A similar question was asked/answered here a couple of months ago: Python state-machine design. You might find looking at those responses useful as well.
I think your solution looks fine, except you should replace count = count - 1 with count -= 1.
This is one of those times where fancy code-show-offs will come up ways of have dicts mapping states to callables, with a small driver function, but it isn't better, just fancier, and using more obscure language features.
I suggest checking out chapter 4 of Text Processing in Python by David Mertz. He implements a state machine class in Python that is very elegant.
I think the most pythonic way would by like what FogleBird suggested, but mapping from (current state, input) to a function which would handle the processing and transition.
You can use regexps. Something like this code will find the first block of data. Then it's just a case of starting the next search from after the previous match.
find_header = re.compile('\xaa\xaa(.).{9}', re.DOTALL)
m = find_header.search(input_text)
if m:
length = chr(find_header.group(1))
data = input_text[m.end():m.end() + length]

Categories

Resources