Scope in python in nested if statements - python

I am writing a small piece of code which parses latex files and gives me the newcommands defined. A test case would be this simple latex file:
% +--------------------------------------------------------------------+
% | |
% | New particle stuff |
% | |
% +--------------------------------------------------------------------+
\newcommand*{\Hmp}{\ensuremath{H^{\mp}}\xspace}
\newcommand*{\susy}[1]{\ensuremath{\tilde{#1}}\xspace}
\newcommand*{\susy2}[1,2]{\ensuremath{\tilde{#1}\tilde{#2}}\xspace}
There might be a more complicated case where the command expands several lines so I need to keep track of different steps like if the command needs to be incremented with more lines or when the command has finished and is ready to be finished.
The thing is that, within a couple of nested if/else statements the scope of the variables seems lost and the variables are not updated anymore. Here is what am I am doing:
macros = []
warg_keep = re.compile("newcommand\*\{(.*)\}\[(.*)\]\{(.*)")
woarg_keep = re.compile("newcommand\*\{(.*)\}\{(.*)")
warg_one = re.compile("newcommand\*\{(.*)\}\[(.*)\]\{(.*)\}")
woarg_one = woarg = re.compile("newcommand\*\{(.*)\}\{(.*)\}")
keep = False
for line in open(file).readlines():
line = line.strip()
if len(line) == 0 or line[0] == "%":
continue
if not keep:
newcommand = {"key":"","command":"","args":[]}
added = False
if "newcommand" in line:
if line[-1] == "%":
clean_line = line[0:-1]
keep = True
newcommand = get_cmd_from_line(warg_keep,woarg_keep,clean_line)
else:
newcommand = get_cmd_from_line(warg_one, woarg_one, line)
added = True
elif keep:
# Now it dos not matter how it ends, the command will always be added the line without the
# last character, it can be either % or } but it shouldn't be added
newcommand["command"] += line[0:-1]
# End the keep
if line[-1] != "%":
keep = False
added = True
elif added:
macros.append(newcommand)
The issue is when I assign the newcommand variable the value I get from the get_cmg_from_line function (which I have tested works perfectly) it doesn't update the newcommand variable but if I move it over the previous if then it recognizes it and updates it. The same thing happens with the keep and added variables.
I have searched for this and found a lot of things about scopes/if/functions etc. which I alrady knew and since ifs shouldn't define scope I don't know why this is happening... am I missing something stupid? How should I update the value of the newcommand variable? Since it might get updated with new lines coming. The only solution I see is to flatten the code but I would like to maintain it like this.
EDIT: I changed a little bit the original code to accommodate extra features of the text but without flattening the code it doesn't work either. So the code on top is not working for the reason I mention. The code below works perfectly and passes all tests:
macros = []
warg_keep = re.compile("newcommand\*\{(.*)\}\[(.*)\]\{(.*)")
woarg_keep = re.compile("newcommand\*\{(.*)\}\{(.*)")
warg_one = re.compile("newcommand\*\{(.*)\}\[(.*)\]\{(.*)\}")
woarg_one = woarg = re.compile("newcommand\*\{(.*)\}\{(.*)\}")
keep = False
for line in open(file).readlines():
line = line.strip()
if len(line) == 0 or line[0] == "%":
continue
if not keep:
newcommand = {"key":"","command":"","args":[]}
added = False
if "newcommand" in line and line [-1] == "%":
clean_line = line[0:-1]
keep = True
newcommand = get_cmd_from_line(warg_keep,woarg_keep,clean_line)
if "newcommand" in line and line[-1] != "%":
newcommand = get_cmd_from_line(warg_one, woarg_one, line)
added = True
if not "newcommand" in line and keep:
# Now it dos not matter how it ends, the command will always be added the line without the
# last character, it can be either % or } but it shouldn't be added
newcommand["command"] += line[0:-1]
if not "newcommand" in line and keep and line[-1] != "%":
# End the keep
keep = False
added = True
if added:
macros.append(newcommand)

On a cursory inspection, my first guess is that elif keep and elif added should be if keep and if added, respectively.
Another possibility is that you are expecting newcommand to accumulate from one line to the next, but you are resetting it on each pass. Should newcommand = { … } be moved in front of the for line in …:?

The scope of local variables in Python (like many other scripting languages) is at function level, and not at block level.
Example:
def function():
x = 5
if True:
y = 8
print(x)
print(y)
function()
# -> 5
# -> 8

Related

How can I keep file pointer in the If clause

Could anybody advice me if there is any way to keep file pointer in the nested IF clause.
I have to parse the file, and based on its content, different code blocks should process the file.
I came up with nested IF loops.
The code:
import re
with open('smartctl.txt', 'r') as file:
line = file.readlines()
for x in line:
matchIP = re.search('Checking', x)
if matchIP:
print(x)
Match_Micron55 = re.search('Micron_5100', x)
Match_Intel = re.search('INTEL', x)
Match_Micron600 = re.search('Micron_M600', x)
Any_Micron = re.search('Micron', x)
if Match_Micron55:
print("here we have Micron55")
elif Match_Intel:
print("here we have Intel")
elif Match_Micron600:
print('here we have Micron 600')
mline = line
print("file is open")
check = ""
for y in mline:
if y == x:
check == True
continue
if y.startswith(' 1 Raw_Read_Error_Rate') and check == True:
print(y)
continue
if y.startswith(' 5 Reallocated_Sector_Ct') and check == True:
print(y)
continue
if y.startswith('173 Unknown_Attribute') and check == True:
print(y)
break
elif Any_Micron:
print('Here we have unsorted values')
As you can see I read the file as line.
Then I go with variable X through the file.
Then, when I fall in IF condition, I have to CONTINUE reading the file: that's keep the file pointer and continue reading exactly from the place I went into the IF loop. I use 2 loops here with X and Y variables (for x in line and for y in mline). Could you tell me please if I can continue reading the same file in the second(nested) If confidition?
The method seems to be non working. Apart from creating Y variable I have also tried using X in the nested IF clause but was not succeed. The x (line) variable seems to not keep its value on the second IF entry.
I suspect your problem lies in the following code:
line = file.readlines()
for x in line:
The fileread is returning a line from the file, the x in line is iterating through the line a character at a time. I bvelieve you should restructure your code as follows:
replace:
with open('smartctl.txt', 'r') as file:
line = file.readlines()
for x in line:
with:
with open('smartctl.txt', 'r') as file:
for x in file.readlines():

Read Lines Depending on Value in Following Lines

I am trying to do a calculation based on the content of a line, but only if another line in the same document satisfies specific criteria. The order of the lines is not consistent.
A file might look like this:
Line A: 200
Line B: 200
Line C: 5
So an example condition would be, if Line C is 6 or greater, add the value from Line A "200" to a counter.
I have tried a variety of if statements, and also tried setting a BOOL. I haven't been able to get either to work. An excerpt of my latest attempt follows:
counter = 0
good = True
for line in text:
line = line.strip()
if line.startswith('Line C') :
rtime = re.findall('[0-9]+:[0-9]+', line)
for t in rtime:
if t < 6 :
good = False
print("-----To Small. Ignore Line A")
break
else :
good = True
while good == True :
if line.startswith('Line A') :
numstring = re.findall('[0-9]+', line)
for num in numstring:
temp = float(num)
counter = counter + temp
else : continue
print("----- good must be False. Should be ignoring Line A")
First, read all the rows from the file into a dictionary so that you have:
{'Line A':200, 'Line B':200, 'Line C':5}
After this it is easy to apply the criterias with conditionals like "if value['Line A'] > 6:" etc.
I am leaving with you the implementation of this because it sounds a bit homework-y. Let me know if you need more help!
Maybe you can use a dictionary if the lines aren't too long. A simple way would just add the lines to a dictionary and then check your condition.
import re
allDataLines = []
allQualifierLines = []
dataFileName = 'testdata.txt'
def chckForQualifierLine(line):
# lines containing qualifier
if not line.startswith('Line C'):
return False
# do more checks here, if not good just return False
allQualifierLines.append(line)
return True
def chckForDataLine(line):
# lines containing data
if not line.startswith('Line A'):
return False
# do more checks here, if not good just return False
allDataLines.append(line)
return True
with open(dataFileName, 'r') as text:
# Further file processing goes here
line = text.readline()
while line != '':
#print(line, end='')
if not chckForQualifierLine(line):
chckForDataLine(line)
line = text.readline()
for qualifierLine in allQualifierLines:
# this line is valid qualifier
print(f"Q: {qualifierLine}")
for dataLine in allDataLines:
# do with data line(s) what ever is to do here
print(f"D: {dataLine}")

Find the last element (digit) on each line and sum all that are even python 3

Hi there Stack Overflow!
I'm trying to solve an assignment we got in my Python class today. I'm not super familiar with python yet so I could really need some tips.
The task is to:
Find the last element (digit) on each line, if there are any, and sum all
that are even.
I have started to do something like this:
result = 0
counter = 0
handle = open('httpd-access.txt')
for line in handle:
line = line.strip()
#print (line)
if line[:-1].isdigit():
print(line[:-1].isdigit())
digitFirst = int(line[counter].isdigit())
if digitFirst % 2 == 0:
print("second")
result += digitFirst
else:
print("else")
ANSWER = result
But this code doesnt work for me, I don't get any data in result.
What is it that i'm missing? Think one problem is that I'm not going through the line element by element, just the whole line.
Here is an example of how I line in the file can look:
37.58.100.166--[02/Jul/2014:16:29:23 +0200]"GET/kod-exempel/source.php?dir=codeigniter/user_guide_src/source/_themes/eldocs/static/asset HTTP/1.1"200867
So the thing I want to retrieve is the 7. And then I want to do a check if the seven is a even or odd number. If it's even, I save it in the a variable.
Don't even bother with the isdigit. Go ahead and try the conversion to int and catch the exception if it fails.
result = 0
with open('httpd-access.txt') as f:
for line in f:
try:
i = int(line.strip()[-1:])
if(i % 2 == 0):
result += i
except ValueError:
pass
print('result = %d' % result)
isdigit() return True or False, which is assigned to digitFirst (try print it!).
True and False are evaluated as 0 and 1 (respectively) in math operations.
So, it always pass the if digitFirst % 2 == 0 when digitFirst is 0, which means 0 always gets added to result.
Also, notice that counter is always 0 during the for loop and gets raised to 1 only after it, which means you are always working with the first letter of every line.
The purpose of counteris unclear as it only used as "the index of the letter you get" each line.
result = []
with open('https-access.txt') as fin:
for line in fin:
l = line.strip()
if l[-1].isdigit():
if int(l[-1]) % 2 == 0:
result.append(int(l[-1]))
ANSWER = sum(result)
How does your file look? You want to calculate the last digit on each line if it's even number. In your code "line[counter]" will catch the first index of each line.
For example if data in file is as follows:
some_data 2
since counter is set to 0, therefore line['counter'] in your code will always check the first index which will be 's' in the example above.
If you can post a few lines from the file that you will be opening, I may be able to suggest something.

if next(item) moves to the next item in a list, what is the eqvilant of next next(item) python

heres the code for context.
def processScores( file, score):
#opens file using with method, reads each line with a for loop. If content in line
#agrees with parameters in elif statements, executes code in if statment. Otherwise, ignores line
with open(file,'r') as f:
for line in f: #starts for loop for all if statements
line = line.strip()
if line.isdigit():
start = int(line)
score.initialScore(start)
print(line)#DEBUG TEST**** #checks if first line is a number if it is adds it to intial score
elif len(line) == 0:
print(line)#DEBUG TEST****
continue #if a line has nothing in it. skip it
elif line == 'o' or line == 'O':
amount = next(f)
print(line)#DEBUG TEST****
score.updateOne(amount) #if line contains single score marker, Takes content in next line and
#inserts it into updateOne
elif line == 'm'or line == 'M':
scoreList = next(f)
lst = []
for item in scoreList:
print(line)#DEBUG TEST****
lst.append(item)
score.updateMany(lst) # if line contains list score marker, creates scoreList variable and places the next line into that variable
# creates lst variable and sets it to an empty list
# goes through the next line with the for loop and appends each item in the next line to the empty list
# then inserts newly populated lst into updateMany
elif line == 'X':
print(line)#DEBUG TEST****
score.get(self)
score.average(self) # if line contains terminator marker. prints total score and the average of the scores.
# because the file was opened with the 'with' method. the file closes after
the idea that I am trying to is work with a file that looks like this:
50
O
30
O
40
M
10 20 30
o
5
m
1 2 3
X
if the code sees an 'O' or an 'o' then it needs to take the next line in the code and add it to a running score.. However the next line is a blank space... So I need to skip to the second line after the 'O' or 'o'.
I was thinking of doing an exception for this, but before I go down that road I wanna see if anyone might know of a better way.
If you want to move along f skipping whitespace-only items,
while True:
x = next(f).strip()
if x: break
will work, as will
for x in f:
x = x.strip()
if x: break
The difference is, what if there is no following non-all-space item in f. The former will exit with a StopIteration exception, the latter exit the for loop with no exception but x set to ''. Pick your poison (which exit form would you rather deal with) and code accordingly!
How about something like:
For line in lines:
if type(line) == 'int':
oneCount += line
elif type(line) == 'list':
manyCount.append(line)
elif type(line) == 'str' and line != 'x':
continue
elif type(line) == None:
continue
else:
print scores
A useful model to think about this problem is a state machine.
The code has 3 states:
Read command code.
Add single score (on "O").
Add multiple scores (on "M").
By keeping a variable with the current state, you can process the input without skipping ahead.
Now, empty lines appear to serve no purpose so you could just remove them all from the input like this:
...
non_empty_lines = (line for line in f if line.strip())
for line in non_empty_lines:
... do your thing ...
The generator expression will filter lines that are all spaces.
If for some reason you cannot use generator expressions, then do it inside the loop:
...
for line in f:
if not line.strip():
continue
...

string comparison failing in python?

I have a small script which goes like this:
(Notice the comparison)
def readVariations(path_to_source_config):
varsTuple = []
source_file = open(path_to_source_config, "r")
for line in source_file:
line_no_spaces = line.replace(" ","")
if line_no_spaces[0] == '[':
current_line_ = line_no_spaces.replace("[", "")
current_line = current_line_.replace("]", "")
section = "ExecutionOptimizer"
if current_line == section:
print current_line
#_tuple = section_name, option_name, range_start, range_end, step
#varsTuple.append(_tuple)
return varsTuple
What it does is reads a config file (.cfg) and needs to check if it finds a particular string.
The following line comes up in the config file:
[ExecutionOptimizer]
For some reason the comparison is failing when the same string is encountered in the file.
Can you please tell me why.
I suspect line ends with a newline character, and it remains there throughout all of your replace operations. Then your comparison fails because "ExecutionOptimizer\n" doesn't equal "ExecutionOptimizer". You can discard the newline using strip:
line_no_spaces = line.strip().replace(" ","")
Use "is" key word.
"==" is for equality testing
From a Python interpreter:
> a = 'tea'
> b = ''.join(['t', 'e', 'a'])
> a == b
True
> a is b
False

Categories

Resources