Using generator to collate user inputs on multiple lines - python

I am trying to save some memory here - I am building a program where the user can input a (stacked) list of integers like:
1
2
3
4
5
.
.
.
The below code works good!
input_list = []
while True:
try:
line = input()
except EOFError:
break
input_list.append(int(line))
print(input_list)
But I would now like to use some sort of generator expression to evaluate the list only when I need, and I almost (argh!) got there.
This code works:
def prompt_user():
while True:
try:
line = input()
except EOFError:
print(line)
break
yield line
input_list = (int(line) for line in prompt_user())
print(list(input_list))
with one only quack: the last integer input by the user is always omitted. So for instance (the ^D is me typing CTRL+D at the console from a pycharm debugger):
1
2
3
4^D
3 # ==> seems like after the EOF was detected the integer on the same
# line got completely discarded
[1, 2, 3]
I don't really know how to go further.

Thanks to #chepner and to this other thread I reduced this whole logic to:
import sys
N = input() # input returns the first element in our stacked input.
input_list = map(int, [N] + sys.stdin.readlines())
print(list(input_list))
leveraging the fact that sys.stdin is already iterable!

Related

reading lines of a file between two words

I have a file containing numbers and 2 words : "start" and "middle"
I want to read numbers from "start" to "middle" in one array and numbers from "middle" to end of the file into another array.
this is my python code:
with open("../MyList","r") as f:
for x in f.readlines():
if x == "start\n":
continue
if x == "middle\n":
break
x = x.split("\n")[0]
list_1.append(int(x))
print list_1
for x in f.readlines():
if x == "middle\n":
continue
list_2.append(int(x))
print list_2
but the problem is that my program never enters second loop and jumps to
print list_2
I searched in older questions but can not figure out the problem.
Its because you are reading the whole at the 1st loop, when it enter 2nd loop, file pointer is already at end of file and you will get an empty list from f.readlines().
You can fix that either by reopen the file or set the file pointer to the beginning of file again with f.seek(0) before the 2nd for loop
with open("../MyList","r") as f:
with open("../MyList","r") as f:
for x in f.readlines():
# process your stuff for 1st loop
# reset file pointer to beginning of file again
f.seek(0)
for x in f.readlines():
# process your stuff for 2nd loop
it will be not so efficient by reading whole file into memory if you are processing large file, you can just iterate over the file object instead of read all into memory like code below
list1 = []
list2 = []
list1_start = False
list2_start = False
with open("../MyList","r") as f:
for x in f:
if x.strip() == 'start':
list1_start = True
continue
elif x.strip() == 'middle':
list2_start = True
list1_start = False
continue
if list1_start:
list1.append(x.strip())
elif list2_start:
list2.append(x.strip())
print(list1)
print(list2)
Your first loop is reading the entire file to the end, but processes only half of it. When the second loop hits, the file pointer is already at the end, so no new lines are read.
From the python docs:
file.readlines([sizehint])
Read until EOF using readline() and return a list containing the lines
thus read. If the optional sizehint argument is present, instead of
reading up to EOF, whole lines totalling approximately sizehint bytes
(possibly after rounding up to an internal buffer size) are read.
Objects implementing a file-like interface may choose to ignore
sizehint if it cannot be implemented, or cannot be implemented
efficiently.
Either process everything in one loop, or read line-by-line (using readline instead of readlines).
You can read the whole file once in a list and later you can slice it.
if possible you can try this:
with open("sample.txt","r") as f:
list_1 = []
list_2 = []
fulllist = []
for x in f.readlines():
x = x.split("\n")[0]
fulllist.append(x)
print fulllist
start_position = fulllist.index('start')
middle_position = fulllist.index('middle')
end_position = fulllist.index('end')
list_1 = fulllist[start_position+1 :middle_position]
list_2 = fulllist[middle_position+1 :end_position]
print "list1 : ",list_1
print "list2 : ",list_2
Discussion
Your problem is that you read the whole file at once, and when you
start the 2nd loop there's nothing to be read...
A possible solution involves reading the file line by line, tracking
the start and middle keywords and updating one of two lists
accordingly.
This imply that your script, during the loop, has to mantain info about
its current state, and for this purpose we are going to use a
variable, code, that's either 0, 1 or 2 meaning no action,
append to list no. 1 or append to list no. 2, Because in the beginning
we don't want to do anything, its initial value must be 0
code = 0
If we want to access one of the two lists using the value of code as
a switch, we could write a test or, in place of a test, we can use a
list of lists, lists, containing a dummy list and two lists that are
updated with valid numbers. Initially all these inner lists are equal
to the empty list []
l1, l2 = [], []
lists = [[], l1, l2]
so that later we can do as follows
lists[code].append(number)
With these premises, it's easy to write the body of the loop on the
file lines,
read a number
if it's not a number, look if it is a keyword
if it is a keyword, change state
in any case, no further processing
if we have to append, append to the correct list
try:
n = int(line)
except ValueError:
if line == 'start\n' : code=1
if line == 'middle\n': code=2
continue
if code: lists[code].append(n)
We have just to add a bit of boilerplate, opening the file and
looping, that's all.
Below you can see my test data, the complete source code with all the
details and a test execution of the script.
Demo
$ cat start_middle.dat
1
2
3
start
5
6
7
middle
9
10
$ cat start_middle.py
l1, l2 = [], []
code, lists = 0, [[], l1, l2]
with open('start_middle.dat') as infile:
for line in infile.readlines():
try:
n = int(line)
except ValueError:
if line == 'start\n' : code=1
if line == 'middle\n': code=2
continue
if code: lists[code].append(n)
print(l1)
print(l2)
$ python start_middle.py
[5, 6, 7]
[9, 10]
$

How to use python to print strings as processing like this

I want to write a function to print out string like this:
Found xxxx...
for x is result calculating by another function. It only print one line, sequential, but not one time. Example: I want to print my_name but it'll be m.... and my.... and my_...., at only line.
Can i do this with python?
Sorry I can't explain clearly by english.
UPDATE
Example code;
import requests
url = 'http://example.com/?get='
list = ['black', 'white', 'pink']
def get_num(id):
num = requests.get(url+id).text
return num
def print_out():
for i in list:
num = get_num(i)
if __name__ == '__main__':
#Now at main I want to print out 2... (for example, first get_num value is 2) and after calculating 2nd loop print_out, such as 5, it will update 25...
#But not like this:
#2...
#25...
#25x...
#I want to it update on one line :)
If you are looking to print your output all in the same line, and you are using Python 2.7, you can do a couple of things.
First Method Py2.7
Simply doing this:
# Note the comma at the end
print('stuff'),
Will keep the print on the same line but there will be a space in between
Second Method Py2.7
import sys
sys.stdout.write("stuff")
This will print everything on the same line without a space. Be careful, however, as it only takes type str. If you pass an int you will get an exception.
So, in a code example, to illustrate the usage of both you can do something like this:
import sys
def foo():
data = ["stuff"]
print("Found: "),
for i in data:
sys.stdout.write(i)
#if you want a new line...just print
print("")
foo()
Output:
Found: stuff
Python 3 Info
Just to add extra info about using this in Python 3, you can simply do this instead:
print("stuff", end="")
Output example taken from docs here
>>> for i in range(4):
... print(i, end=" ")
...
0 1 2 3 >>>
>>> for i in range(4):
... print(i, end=" :-) ")
...
0 :-) 1 :-) 2 :-) 3 :-) >>>
s = "my_name"
for letter in range(len(s)):
print("Found",s[0:letter+1])
Instead of 's' you can just call function with your desired return value.

Transform an algebraic expression with brackets into RPN (Reverse Polish Notation) form

from sys import stdin
t=int(stdin.readline())
while(t):
s=stdin.readline()
str = []
top=-1
for i in range(0, len(s)):
c=s.index(i)
if(c>='a' and c<='z'):
print(c)
elif(c=='('):
pass
elif(c==')'):
print(str[top])
top-=1
else:
top+=1
str[top] = c
t-=1
Input:
1
(a+b)
Error:
Traceback (most recent call last):
File "C:\Users\Anurag\AppData\Roaming\NetBeans\8.0.1\nb_test_runner.py", line 216, in <module>
module = __import__(module_name, globals(), locals(), module_name)
File "__pyclasspath__/opn.py", line 8, in <module>
Finished in 0.0 seconds.
TypeError: expected a str
0 tests, 0 failures, 0 errors
After providing 1 and (a+b) as input above error get displayed.
The reported error is happening because s.index() doesn't do what you think it does. s.index(substr) returns the index of substr in s. See the docs for details. Try
c = s[i]
or even better, change the start of the for loop to
for c in s:
There are a few other problems with your code. Eg, str[top] will fail if str is an empty list.
The code below will run, but the zstr = [None]*20 line is a band-aid solution & you really need to use better logic here. Also, your current algorithm requires expressions to be parenthesized, which is a bit limiting.
from sys import stdin
t = int(stdin.readline())
while t:
s = stdin.readline()
zstr = [None]*20
top = -1
for c in s:
if c.islower():
print(c)
elif c=='(':
pass
elif c==')':
print(zstr[top])
top -= 1
else:
top += 1
zstr[top] = c
t-=1
Test
echo -e "2\n(x-y)\n((a+b)*(c+d))" | python qtest.py
Output
x
y
-
a
b
+
c
d
+
*
Edit
An efficient way to get all the output on one line is to gather the output strings into a list, and then join them into one string. OTOH, just keeping them in a list may be useful.
Also, it's a good idea to keep your processing logic separate from input and output, where practical. Of course, for a calculator program, that may not be practical.
rpntest.py
#! /usr/bin/env python
''' Transform an algebraic expression with brackets into RPN (Reverse Polish Notation) form
From http://stackoverflow.com/questions/26191707/transform-an-algebraic-expression-with-brackets-into-rpn-reverse-polish-notatio
'''
import sys
import readline
def to_rpn(s):
rpn = []
zstr = [None] * 20
top = -1
for c in s:
if c.islower():
rpn.append(c)
#print c
elif c=='(':
pass
elif c==')':
rpn.append(zstr[top])
#print zstr[top]
top -= 1
else:
top += 1
zstr[top] = c
return ' '.join(rpn)
def main():
#for line in sys.stdin:
while True:
try:
line = raw_input()
except EOFError:
break
if line == '':
continue
rpn = to_rpn(line)
print rpn
if __name__ == '__main__':
main()
I've changed the input logic of the program a little. Now you don't need to specify how many expressions to transform. The program still reads one algebraic expression per line, but it ignores blank lines. By importing readline, it also gives you some line editing ability, so the arrow keys can be used. To exit the program you need to send an End Of File signal - on Linux that's Ctrl-D, I think on Windows it's Ctrl-Z. You can still pipe input into the program, eg echo -e "(x-y)\n((a+b)*(c+d))" | python rpntest.py.

How to scan an unknown number of numbers in Python?

Is there a way to scan an unknown number of elements in Python (I mean scan numbers until the user writes at the standard input eof(end of file))?
raw_input (input in Python 3) throws EOFError once EOF is reached.
while 1:
try:
num = int(raw_input("enter a number: "))
except EOFError:
break
Do you need to process the first number before the second is entered? If not, then int(s) for s in sys.stdin.read().split() would probably do, either as a list comprehension (in []) or generator expression (in (), for example as a function argument).
This would break at any
Here is one primitive way to do it.
In [1]: while True:
...: try:
...: num = int(raw_input())
...: except EOFError:
...: break
...:
Example output for the input that I had given:
10
20
40
50
This would break not just for EOF but for any input which does not convert to number.
I found another way... sometimes, I use a different programming language called R.
There, you can enter:
my_variable = scan()
...and enter numbers as you wish. Even with copy & paste.
Then, you can dump the input:
dump(my_variable, "my_variable.Rdata")
my_variable.Rdata is a text file which contains an R list... which can be easily converted into a Python list:
# a list in R syntax:
my_variable <- c(1, 2, 3)
# a list in Python syntax:
my_variable = [1, 2, 3]

GoTo(basic) program

I'm doing some tutorials online, and I'm stuck with an exercise :Write a function getBASIC() which takes no arguments, and does the following: it should keep reading lines from input using a while loop; when it reaches the end it should return the whole program in the form of a list of strings. Example of list of strings:
5 GOTO 30
10 GOTO 20
20 GOTO 10
30 GOTO 40
40 END
I wrote a program, but it doesn't work, however I will post it too:
def getBASIC():
L=[]
while "END" not in L:
L.append(str(input()))
if str(input()).endswith("END"):
break
return L
Also I notice you that I'm not allowed to use IS or RECURSION .
There are several errors:
you call input() twice without appending it to the list the second time
'END' in L determines whether there is 'END' (whole) line in the list L (there isn't)
Note: input()already returns a str object; you don't need to call str() on its returned value.
To read input until you've got empty line you could:
def getBASIC():
return list(iter(input, ''))
Or to read until END is encountered at the end of line:
def getBASIC():
L = []
while True:
line = input()
L.append(line)
if line.endswith("END"):
break #NOTE: it doesn't break even if `line` is empty
return L
Back when I was learning Pascal, we used a priming read for loops that needed at least one iteration. This still works well in Python (I prefer it to a while True / break loop).
By simply testing the last line in the list (rather than the last line read) we eliminate the need for a variable to store the input and can combine the reading and appending operations.
def getBASIC():
lines = [input("]")] # use Applesoft BASIC prompt :-)
while not lines[-1].upper().rstrip().endswith("END"):
lines.append(input("]"))
return lines
try this one:
def get_basic():
L = []
while True:
line = str( input() )
L.append( line )
if "END" in line:
break
return L
Use raw_input() instead of input(). input() function takes string from standard input and tries to execute it as a python source coode. raw_input() will return a string as expected.
You use input() 2 time inside a loop. That means you await two string to be input inside one cycle iteration. You don't need last condition (if statement) inside your while loop. It'll end up when "END" is encountered in L.
The next code should do the job:
def getBASIC():
L=[]
while True:
inp = raw_input()
L.append(inp)
if inp.endswith('END'):
break
return L
Your code has the following problems.
"while "END" not in L" will not work if your input is "40 END"
In Python 2.7, "input()" is equivalent to "eval(raw_input()))". So, Python is trying to evaluate the "GOTO" statements.
"if str(input()).endswith("END"):" does not append input to L
So, here is an updated version of your function:
def getBASIC():
L = []
while True:
# Grab input
L.append(str(raw_input()))
# Check if last input ends with "END"
if L[-1].endswith("END"):
break
return L

Categories

Resources