Python: Change variable suffix with for loop - python

I know this was asked a lot but I can not work with/understand the answers so far.
I want to change the suffix of variables in a for loop.
I tried all answers the stackoverflow search provides. But it is difficult to understand specific codes the questioner often presents.
So for clarification I use an easy example. This is not meant as application-oriented. I just want to understand how I can change the suffix.
var_1 = 10
var_2 = 100
var_3 = 1000
for i in range(1,4):
test_i = var_i + 1
print(test_i)
Expected result:
creating and printing variables:
test_1 = 11
test_2 = 101
test_3 = 1001
Expected Output
11
101
1001
Error: var_i is read as a variable name without the changes for i.

I would advise against using eval in 99.99% of all cases. What you could do is use the built-in getattr function:
import sys
var_1 = 10
var_2 = 100
var_3 = 1000
for i in range(1,4):
test_i = getattr(sys.modules[__name__], f"var_{i}") + 1
print(test_i)

Instead of doing a convoluted naming convention, try to conceive of your problem using a data structure like dictionaries, for example.
var={}
var[1] = 10
var[2] = 100
var[3] = 1000
test={}
for i in range(1,4):
test[i] = var[i] +1
print(test)
If somehow you are given var_1 etc as input, maybe use .split("_") to retrieve the index number and use that as the dictionary keys (they can be strings or values).
Small explanation about using indexing variable names. If you are starting out learning to program, there are many reasons not to use the eval, exec, or getattr methods. Most simply, it is inefficient, not scalable, and is extremely hard to use anywhere else in the script.
I am not one to insist on "best practices" if there is an easier way to do something, but this is something you will want to learn to avoid. We write programs to avoid having to type things like this.
If you are given that var_2 text as a starting point, then I would use string parsing tools to split and convert the string to values and variable names.
By using a dictionary, you can have 1000 non-consecutive variables and simply loop through them or assign new associations. If you are doing an experiment, for example, and call your values tree_1, tree_10 etc, then you will always be stuck typing out the full variable names in your code rather than simply looping through all the entries in a container called tree.
This is a little related to using a bunch of if:else statements to assign values:
# inefficient way -- avoid
if name == 'var_1' then:
test_1=11
elif name == 'var_2' then:
test_2=101
It is so much easier just to say:
test[i]= var[i]+1
and that one line will work for any number of values.

for i in range(1, 4):
print(eval('var_' + str(i)))
Step by step:
1) Make your variables strings:
stringified_number = str(i)
2) evaluate your expression during runtime:
evaluated_variable = eval('var_' + stringified_number)

Related

Using variables as function parameters in VBA, specifically Cells() Function

I am working with loops & arrays in VBA.
I watched a bunch of videos online and none of the instructors have gone over this.
In Python, you can dynamically access values from an array by setting the index of my variable:
array1 = [1,2,3,4,5]
b = 0
while(b < len(array1)):
print(array1[b])
b = b + 1
You can do something similar with pandas indexing iloc capability....this way I can change the variable _currentRow7 to whatever I want....I just need to do _currentRow7 = _currentrow7+1
Array.iloc[_currentRow7, 11])
With the VBA Cells function, I get an error. Essentially I want to access values from Excel dynamically. For example - Cells(variable, 1)....then as the variable changes, I am accessing the value in the next cell.
Sub HomeRunCounterFNCTN()
Dim HomeRuns(27) As Integer
Dim HRCounter As Variant
Worksheets("Baseball").Activate
Range("L3").Activate
For HRCounter = 0 To 27
HomeRuns(HRCounter) = ActiveCell.Offset(HRCounter, 0).Value
If (HomeRuns(HRCounter) >= 45) Then MsgBox (HomeRuns(HRCounter))
MsgBox (Cells(HRCounter & 1))
Next HRCounter
End Sub
Essentially I would like the MsgBox (Cells(HRCounter & 1)) to update dynamically as the variable HRCounter changes.
I was able to run your code without error, so as #Tim Williams says, it would be helpful to describe the error you are getting.
I did spot one non-fatal flaw in this line:
MsgBox (Cells(HRCounter & 1)
You probably meant Cells(HRCounter, 1). The typo doesn't cause a program error, but the line fails to capture what you intended to capture.
Aside from the typo, the code seems to do what you intended it to do. However, to answer your question more directly, you can certainly work with variable cell references. In basic pattern, simply use SomeWorksheet.Cells(MyVar,1).Value, where MyVar is the variable holding your row number.
To write efficient VBA code, learn to avoid Activate and Select whenever possible. Think of them as code imitating the actions of a human user, which is rarely required. Instead, work directly with Excel objects and their properties. Your code can be recast as follows, which will also make the use of a variable row reference more visible:
Sub HomeRunCounterFNCTN()
Dim HomeRuns(27) As Integer, ReadRow As Integer
Dim HRCounter As Variant
With Worksheets("Baseball")
For HRCounter = 0 To 27
ReadRow = HRCounter + 3
HomeRuns(HRCounter) = .Cells(ReadRow, 12).Value
If (HomeRuns(HRCounter) >= 45) Then MsgBox HomeRuns(HRCounter)
Next HRCounter
End With
End Sub

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")

Split string in multiple places

New to programming and currently working with python. I am trying to take a user inputted string (containing letters, numbers and special characters), I then need to split it multiple times at different points to reform new strings. I have done research on the splitting of strings (and lists) and feel I understand it but I still know there must be a better way to do this than I can think of.
This is what I currently have
ass=input("Enter Assembly Number: ")
#Sample Input 1 - BF90UQ70321-14
#Sample Input 2 - BS73OA91136-43
ass0=ass[0]
ass1=ass[1]
ass2=ass[2]
ass3=ass[3]
ass4=ass[4]
ass5=ass[5]
ass6=ass[6]
ass7=ass[7]
ass8=ass[8]
ass9=ass[9]
ass10=ass[10]
ass11=ass[11]
ass12=ass[12]
ass13=ass[13]
code1=ass0+ass2+ass3+ass4+ass5+ass6+ass13
code2=ass0+ass2+ass3+ass4+ass5+ass6+ass9
code3=ass1+ass4+ass6+ass7+ass12+ass6+ass13
code4=ass1+ass2+ass4+ass5+ass6+ass9+ass12
# require 21 different code variations
Please tell me that there is a better way to do this.
Thank you
Give a look to this code and Google "python string slicing" (a nice tutorial for beginners is at https://www.youtube.com/watch?v=EqAgMUPRh7U).
String (and list) slicing is used a lot in Python. Be sure to learn it well. The upper index could be not so intuitive, but it becomes second nature.
ass="ABCDEFGHIJKLMN"
code1 = ass[0] + ass[2:7] + ass[13] # ass[2:7] is to extract 5 chars starting from index 2 (7 is excluded)
code2 = ass[0] + ass[3:7] + ass[9]
code3 = ass[1] + ass[4] + ass[6:8] + ass[12] + ass[6] + ass[13]
code4 = ass[1:3] + ass[4:7] + ass[9] + ass[12]
PS: You probably need also to check if the string length is 14 before working with it.
EDIT: Second solution
Here is another solution, perhaps it is easier to follow:
def extract_chars(mask):
chars = ""
for i in mask:
chars += ass[i]
return chars
mask = [0,2,3,4,5,6,13]
print extract_chars(mask)
Here you define a mask of indexes of the chars you want to extract.
You can try something like this,
input1 = 'BF90UQ70321-14'
code = lambda anum, pos: ''.join(anum[p] for p in pos)
code4 = code(input1, (1,2,4,5,6,9,12))

print different variables at one time in python

Have a question about how to print several separate variable with one command in python. E.g.I have A0=5,A1=6,A2="what",A3=[1,2,3],A4=(10,100)
I can do print(A0), print(A1), print(A2), etc
But what if I have 100 such variable which starting with same character (in this case is 'A'), how to print them out with one command?
Though I don't think that it is a good idea, but yes, you can do it:
A0 = 5
A1 = 2
A3 = 232
.
..
A999 = 25
for i in range(1000):
line = 'print(A{0})'.format(i)
exec(line)
if you "have 100 such variables which starting with same character (in this case is 'A')" and have a need to treat them as a collection, then obviously the proper solution is to use a collection instead of distinct "A0", "A1", ... "AN" variables. Whenever you end up doing something awkward (like here), ask yourself if your data are properly structured. Chances are it's not.

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