Unable to index into a list - python

I am writing a program to read the output of another program, read it line by line and put it in a list.
#!/usr/bin/python
import subprocess
def RECEIVE(COMMAND):
PROCESS = subprocess.Popen(COMMAND, stdout=subprocess.PIPE)
LINES = iter(PROCESS.stdout.readline, "")
for LINE in LINES:
RECARR = LINE.split()
print RECARR[14]
RECEIVE(["receivetest","-f=/dev/pcan32"])
The output from the receivetest program is:
19327481.401 receivetest: m s 0x0000000663 8 2f 00 42 02 00 e4 8a 8a
19327481.860 receivetest: m s 0x000000069e 8 00 1f 5e 28 34 83 59 1a
it is a constant stream of messages. When split, the list has a range of 14 because after splitting, to make sure, I used:
print len(RECARR)
This gave me an output of 14.
but whenever I try to print the last element:
print RECARR[14]
I get the following error:
file "./cancheck.py", line 10, in RECEIVE
print RECARR[14]
IndexError: list index out of range
This is caused by some erronious text that is printed at the top of the list, so I need some way of making sure that the program only reads in lines that start with
1234567.123
/^(.......\.\d{1,3}) (.*)$/
Any ideas?

Based on the sample data you provided, the length of RECARR is always 14.
14 is the size of the list, not the maximum index. To get the final element of the array, you can try RECARR[13] for this list, or RECARR[-1] in general.
The reason for this is that in Python, as in most programming languages, array indices are zero-based: the first element is accessed with RECARR[0], the second with RECARR[1], and so on. So, the 14th element (or the last one, in your case) would be accessed with RECARR[13].
So, your for loop would look something like this:
for LINE in LINES:
RECARR = LINE.split()
print RECARR[13] # or RECARR[-1]

Right everyone, it's a terrible workaround but I fixed the issue by working out that the only lines with exactly 14 elements are the lines I need so I fixed it by using the following
for LINE in LINES:
RECARR = LINE.split()
if(len(RECARR) == 14):
#do stuff

List indexes start from 0 and not 1. So
print RECARR[1]
prints the 2nd element and not the first. Thus to print the last element you have to use print RECARR[13] or negative index print RECARR[-1].
The lists in python can be depicted as
As you can see the last element can be accessed using either -1 or length of the list -1
An easier way to gauge the ranges is to put the indices before the cell. (Courtesy - Aristide)
+---+---+---+---+---+---+
| P | y | t | h | o | n |
+---+---+---+---+---+---+
0 1 2 3 4 5 6
-6 -5 -4 -3 -2 -1

You could have also done something similar to this:
try:
print RECARR[13]
except IndexError:
pass
This way you can easily handle the lines that are not long enough as well.

Related

How to split a byte into its upper and lower halves

I am writing a Python program where I process a file byte-by-byte, and I am trying to write a function that splits a byte into its upper and lower halves. To elaborate, let's say I want to run this function on the byte with the decimal value 18 and the hexadecimal value 12. I would want it to be split into two bytes with values of 1 and 2.
Here is a function I wrote to do this:
# split byte into upper and lower halves
def splitByte(b):
lowerMask = b'\x0F'
lowerHalf = bytes(b & lowerMask[0])[0]
upperMask = b'\xF0'
upperHalf = bytes(b & upperMask[0])[0]
upperHalf = upperHalf >> 4
return [upperHalf,lowerHalf]
Here is where I am calling the function:
info = stream.read(1)
result = splitByte(info[0])
print(result)
However, when I run a file with just the above code and the function, the following occurs:
[0, 0]
[0, 0]
[0, 0]
[0, 0]
Traceback (most recent call last):
File "./test.py", line 8, in <module>
result = splitByte(info[0])
File "<home folder>/byteops.py", line 21, in splitByte
lowerHalf = bytes(b & lowerMask[0])[0]
IndexError: index out of range
Not only is the function returning 0 for both values, but it errors out on some inputs, with an 'index out of range' error. For context, here is the file I'm reading from, as viewed in a hex editor:
00000000: 4C 49 54 35 30 0A 09 09 02 01
I am running Manjaro Linux with Python 3.7.1. How should I fix my splitByte function, or is there a library function that does it for me?
Your problem is that converting from int to bytes. bytes(2) is a request for a bytearray of two zeros. You can simply use the int manipulations you already know:
# split byte into upper and lower halves
def splitByte(b):
lowerHalf = b & 15
upperHalf = (b >> 4) & 15
return [upperHalf,lowerHalf]
result = splitByte(18)
print(result)
Output:
[1, 2]
I left this as integers, since your original program needed only the byte division, not a bytearray.
There is a much simpler way to do that. You can use the ord function to convert a single character to its ASCII value (in base 10). Then, you use the hex function to convert this value into an hexadecimal value (in string). You can now easily access to the upper and lower part of your value.
Here is an example:
val = 'a'
print(hex(ord(val))[2]) # 6
print(hex(ord(val))[3]) # 1
You get 6 and 1 because hexadecimal value of a is 0x61.
Now, if you directly get the decimal value of each character of your source file, you can get rid of the ord function:
val = 97
print(hex(val)[2]) # 6
print(hex(val)[3]) # 1

Cannot understand specific Python 3 code

While trying to solve a problem on Hackerank(The Problem). I checked the solutions of people who have solved this problem. Here is the solution from one person-
n = input()
l = []
for _ in range(n):
s = raw_input().split()
cmd = s[0]
args = s[1:]
if cmd !="print":
cmd += "("+ ",".join(args) +")"
eval("l."+cmd)
else:
print l
I cannot understand line 8 and line 9, can someone explain me these lines? Can i write line 8 and line 9 in Python 3 as i'm learning Python 3 ? How ?
Basically, cmd is constructed by appending the command (say "insert"), to the operands. This cmd forms a correct python expression (for example l.insert(0,5), to insert 5 at index 0, in list l).
Here, l. is hardcoded(start of line 9), cmd is initialized in cmd = s[0], and operands are added in line 8.
eval(str) evaluates the command str, in string format, as if it were in a command line.
It would be nice to just include the problem :) . The input is a (text) file as below:
Sample Input 0
12
insert 0 5
insert 1 10
insert 0 6
print
remove 6
append 9
append 1
sort
print
pop
reverse
print
And the expected output for a correct answer is as below:
Sample Output 0
[6, 5, 10]
[1, 5, 9, 10]
[9, 5, 1]
Before looking at the answer you quoted it would be good to read about eval; it takes the argument provided and tries to run it as a python expression using the global and local namespace. So in this case it needs only the local for the "l"-list and "cmd"-tuple.
What is happening is the following:
Empty list l is created.
The "command" (cmd) single-value list is parsed from the line by slicing (cmd = s[0]), since every line starts with or only has a list method
The other arguments are placed in args
Line 8 (as asked): These other arguments are then joined in a string tuple. So "insert 0 5" gives "insert" for l and "(0, 5)" for cmd
Line 8 continued (as asked): cmd is then combined with args using string concatenation (read here for a good and bad example) resulting in "insert(0,5)" as value for cmd
Line 9 (as asked): the eval parameter is yet another string concatenation yielding "l.insert(0,5)" as final expression to be interpreted. Which then inserts integer value 5 on spot 0 in list l (pushing forward any other values already in l)
Hope it helps, keep on trucking!

numpy.where produces inconsistent results

I have a piece of code where I need to look for an index of a value in a numpy array.
For this task, I use numpy.where.
The problem is that numpy.where produces a wrong result, i.e. returns an empty array, in situations where I am certain that the searched value is in the array.
To make things worse, I tested that the element is really in the array with a for loop, and in case it is found, also look for it with numpy.where.
Oddly enough, then it finds a result, while literally a line later, it doesnt.
Here is how the code looks like:
# progenitors, descendants and progenitor_outputnrs are 2D-arrays that are filled from reading in files.
# outputnrs is a 1d-array.
ozi = 0
for i in range(descendants[ozi].shape[0]):
if descendants[ozi][i] > 0:
if progenitors[ozi][i] < 0:
oind = outputnrs[0] - progenitor_outputnrs[ozi][i] - 1
print "looking for prog", progenitors[ozi][i], "with outputnr", progenitor_outputnrs[ozi][i], "in", outputnrs[oind]
for p in progenitors[oind]:
if p == -progenitors[ozi][i]:
# the following line works...
print "found", p, np.where(progenitors[oind]==-progenitors[ozi][i])[0][0]
# the following line doesn't!
iind = np.where(progenitors[oind]==-progenitors[ozi][i])[0][0]
I get the output:
looking for prog -76 with outputnr 65 in 66
found 76 79
looking for prog -2781 with outputnr 65 in 66
found 2781 161
looking for prog -3797 with outputnr 63 in 64
found 3797 163
looking for prog -3046 with outputnr 65 in 66
found 3046 163
looking for prog -6488 with outputnr 65 in 66
found 6488 306
Traceback (most recent call last):
File "script.py", line 1243, in <module>
main()
File "script.py", line 974, in main
iind = np.where(progenitors[oind]==-progenitors[out][i])[0][0]
IndexError: index 0 is out of bounds for axis 0 with size 0
I use python 2.7.12 and numpy 1.14.2.
Does anyone have an idea why this is happening?

How can I create a decremented numberPyramid(num) in Python?

I'm trying to create a pyramid that looks like the picture below(numberPyramid(6)), where the pyramid isn't made of numbers but actually a black space with the numbers around it. The function takes in a parameter called "num" and which is the number of rows in the pyramid. How would I go about doing this? I need to use a for loop but I'm not sure how I implement it. Thanks!
666666666666
55555 55555
4444 4444
333 333
22 22
1 1
def pyramid(num_rows, block=' ', left='', right=''):
for idx in range(num_rows):
print '{py_layer:{num_fill}{align}{width}}'.format(
py_layer='{left}{blocks}{right}'.format(
left=left,
blocks=block * (idx*2),
right=right),
num_fill=format((num_rows - idx) % 16, 'x'),
align='^',
width=num_rows * 2)
This works by using python's string format method in an interesting way. The spaces are the string to be printed, and the number used as the character to fill in the rest of the row.
Using the built-in format() function to chop off the leading 0x in the hex string lets you build pyramids up to 15.
Sample:
In [45]: pyramid(9)
999999999999999999
88888888 88888888
7777777 7777777
666666 666666
55555 55555
4444 4444
333 333
22 22
1 1
Other pyramid "blocks" could be interesting:
In [52]: pyramid(9, '_')
999999999999999999
88888888__88888888
7777777____7777777
666666______666666
55555________55555
4444__________4444
333____________333
22______________22
1________________1
With the added left and right options and showing hex support:
In [57]: pyramid(15, '_', '/', '\\')
ffffffffffffff/\ffffffffffffff
eeeeeeeeeeeee/__\eeeeeeeeeeeee
dddddddddddd/____\dddddddddddd
ccccccccccc/______\ccccccccccc
bbbbbbbbbb/________\bbbbbbbbbb
aaaaaaaaa/__________\aaaaaaaaa
99999999/____________\99999999
8888888/______________\8888888
777777/________________\777777
66666/__________________\66666
5555/____________________\5555
444/______________________\444
33/________________________\33
2/__________________________\2
/____________________________\
First the code:
max_depth = int(raw_input("Enter max depth of pyramid (2 - 9): "))
for i in range(max_depth, 0, -1):
print str(i)*i + " "*((max_depth-i)*2) + str(i)*i
Output:
(numpyramid)macbook:numpyramid joeyoung$ python numpyramid.py
Enter max depth of pyramid (2 - 9): 6
666666666666
55555 55555
4444 4444
333 333
22 22
1 1
How this works:
Python has a built-in function named range() which can help you build the iterator for your for-loop. You can make it decrement instead of increment by passing in -1 as the 3rd argument.
Our for loop will start at the user supplied max_depth (6 for our example) and i will decrement by 1 for each iteration of the loop.
Now the output line should do the following:
Print out the current iterator number (i) and repeat it itimes.
Figure out how much white space to add in the middle.
This will be the max_depth minus the current iterator number, then multiply that result by 2 because you'll need to double the whitespace for each iteration
Attach the whitespace to the first set of repeated numbers.
Attach a second set of repeated numbers: the current iterator number (i) repeated itimes
When your print characters, they can be repeated by following the character with an asterisk * and the number of times you want the character to be repeated.
For example:
>>> # Repeats the character 'A' 5 times
... print "A"*5
AAAAA

Python if check error

When I use the code:
def Jack():
global PHand
if 11 or 24 or 37 or 50 in PHand:
PHand.remove(11 or 24 or 37 or 50)
PHand.append("Jack")
I get an error saying list.remove(x) x is not in PHand, my question is, shouldn't the if check prevent this error?
You're basically checking to see whether 11 is true. It's non-zero, so your if always executes. What you want is:
if 11 in PHand or 24 in PHand or 37 in PHand or 50 in Phand:
Of course, your PHand.remove always tries to remove 11 for much the same reason. You can't tell remove to remove any of those (not sure where you got the idea that would even work, it's not in any documentation I've ever seen), so you should structure it so:
if 11 in PHand:
PHand.remove(11)
PHand.append("Jack")
if 24 in PHand:
PHand.remove(24)
PHand.append("Jack")
... and so on.
Of course you'd be better off refactoring that into a loop or even a function, rather than repeating all that code.
You need to iterate over each element:
for i in (11, 24, 37, 50): # assign i to 11, then 24, then 37, then 50
if i in PHand: # check each one in PHand
PHand.remove(i) # and remove that one
PHand.append("Jack") # your code
break # end the loop. remove this to check all
Otherwise, 11 or 24 or 37 or 50 in PHand outputs 11. Try it!
>>> 11 or 24 or 37 or 50 in PHand
11
Why? the way or works, it checks if the first side is truthy. If it is, it doesn't bother evaluating the rest, since the result couldn't change. If it weren't truthy, it would move on to the next argument, and so on.
And what of the in PHand? That actually gets evaluated first, to just the last number like this:
11 or 24 or 37 or (50 in PHand)
But again, 11 short-circuits all the ors.
Long story short:
or always returns a single value, not all values at once applied to functions repeatedly or however your syntax implies.
Just another way of solving it using filter:
def Jack():
T = [11,24,37,50]
found = filter(lambda x:x in T, PHand)
if found:
[PHand.remove(x) for x in found]
PHand.append('Jack')
PHand = range(10,55)
Jack()

Categories

Resources