Assignments in for loops - python

This is something I wish to ask on top of the question that is already discussed:
Assigning values to variables in a list using a loop
So the link above says it is not recommended to perform assignments in a for loop because doing so changes only the reference of those values and doesn't change the original variable.
For example, if I wish to add 1 to each variable:
p = 1
q = 2
r = 3
a = [p,q,r]
for i in a:
i += 1
print(p, q, r)
# returns 1 2 3
So this doesn't change p, q, and r, since the values added by one are assigned to 'i'. I inquired some people about a possible solution to this issue, and one suggested that I try the 'enumerate' function. So I did:
p = 1
q = 2
r = 3
a = [p,q,r]
for idx, val in enumerate(a):
a[idx] = val + 1
print(p, q, r)
# returns 1 2 3
So it still doesn't work. Printing the list 'a' does return the list of values added by 1, but it still doesn't change the values assigned to p, q, and r, which is what I want.
So the only solution I currently have is to do the assignment for each variable manually:
p = 1
q = 2
r = 3
p += 1
q += 1
r += 1
print(p, q, r)
# returns 2 3 4
However, in a hypothetical setting where there are more variables involved than 3, such as 50 variables where I wish to add 1 to each value assigned, doing it manually is going to be very demanding.
So my question is, is there a solution to this without doing it manually? How do we accomplish this using the for loop? Or, if the for loop doesn't work for this case, is there another way?

You can maintain a dictionary, where the keys are strings (the names variables that you originally had) and the values are the integers that they're assigned to.
Then, you can do the following:
data = {
"p": 1,
"q": 2,
"r": 3
}
for item in data:
data[item] += 1
print(data)
This outputs:
{'p': 2, 'q': 3, 'r': 4}

Related

Are there any shorter way for this way?

Lets say i have variables a,b,c,d,e,f.. every time 2 of the 6 variables will have value = 0 randomly. So my code is like this
if(a == 0 and b == 0):
run c,d,e,f
elif(a == 0 and c == 0):
run b,d,e,f
...
...
continue until end of all combination
So the coding will be very long, are there any other approach ?
You can put all numbers into a list and then feed a list comp of that list into the run function - ignoring elements that are 0:
def run(p1,p2,p3,p4):
print(p1,p2,p3,p4)
# 3 test cases
for d in [ [ 1,2,0,3,4,0], [0,0,2,3,4,1], [4,3,0,2,1,0]]:
run(*[x for x in d if x]) # *[1,2,3] makes python provide the elements as params
Output:
1 2 3 4
2 3 4 1
4 3 2 1
run( *[1,2,3]) is the same as run(1,2,3)
0 is Falsy - so *[x for x in d if x] for a d=[0,1,2,3,0] does only use non falsy values of x in d: *[1,2,3]
truth value testing
you can exchange the list comp run(*[x for x in d if x]) against a generator comp if you like run(*(x for x in d if x)) to avoid the list creation (not that it matters here ;) )
#Mehrdad Dowlatabadi raised an interesting side question - if any other of the params is 0 you get an error due to a mismatch between function parameters and provided parameters from the list comprehension - you can negate that by defining defaults:
def run(p1=0, p2=0, p3=0, p4=0):
print(p1,p2,p3,p4)
So if you feed [0,1,2,0,0,0] into it it will still run.
If you want to run a function with the variables that aren't set to 0 you can first make a list of elements that are'nt 0
elements = [element for element in a, b, c, d, e if element !=0]
then call the function with elements list as arguments
run(*elements)
As a one liner :
run(*[element for element in a, b, c, d, e if element !=0])
Make run take a list:
def run(lst):
...
then use the filter function:
run(filter(None, [a, b, c, d, e, f]))
filter(None, lst) removes all fals-y elements.

Complex behaviour with lists

I need to make some strange and complex calculation with lists. I have tried and I have endeavored to get it up and running, but it runs into error. better saying quite difficult to achieve that behavior.
I have following lists.
A = [1,1,1,2,2,2]
B = [3,3] # B list is length of numbers 1 and 2.
E = [10,10]
C = 1
D = []
I have this code, but not really working:
for k in B:
for j in E:
for i in range(len(A)-k):
print(i)
if i == 0:
D.append(C)
else:
D.append(C+(E[k]))
print(D)
Explaining to achieve results.
I want to have a for-loop, which enables to append values to my empty list, which looks at first 3 values in the beginning of list A by taking B[0]= 3, do something with first 3 values. And looks at B[1]= 3, ie. take the last 3 values in the list A, then do something to them and append them all in order to empty list.
First 3 values:
When A[0] is selected, I want to have D[0] = C, and in case A[1] and A[2], B list should be B[1]= C + 1*E[0] and B[2]= C + 2*E[0].
Last 3 values:
When A[3] is selected, I want to have D[3] = C, and in case A[4] and A[5], B list should be B[4]= C + 1*E[1] and B[5]= C + 2*E[1].
Expected output:
[1,11,21,1,11,21]
I want to get it programmatically, in case changing A list to A = [1,1,2,2] and B = [2,2] or something else.
Initialization of your lists
A = [1,1,1,2,2,2]
B = [3,3] # B list is length of numbers 1 and 2.
E = [10,10]
C = 1
D = []
we want to count in A starting the first time from 0, the next times from the previous start plus how many items we have used, hence we initialize start
start = 0
We start a loop on the elements b of B, counting them in k, we extract from A the elements we need and update the start position for the next pass
for k, b in enumerate(B):
sub_A = A[start:start+b]
start = start+b
Now an inner loop on the elements a of the sub-list, counting them with i, note that for the first item i is zero and so we append C+0*E[k]=C, as requested
for i, _ in enumerate(sub_A):
D.append(C+i*E[k])
To see everything without my comments
start = 0
for k, b in enumerate(B):
sub_A = A[start:start+b]
start = start+b
for i, _ in enumerate(sub_A):
D.append(C+i*E[k])

Iterating through functions using for loop

I currently have multiple functions as below:
vect_1 = CountVectorizer(parameters)
vect_2 = CountVectorizer(parameters)
vect_3 = CountVectorizer(parameters)
vect_3 = CountVectorizer(parameters)
which I am trying to iterate each one of them. I've tried:
for i in range(4):
vect = vect_[i]
print vect
And I am struggling to correctly defining 'vect' part as it just becomes a string. Any ideas please?
Thanks
This is the pythonic way to do it, using a list:
vects = [
CountVectorizer(parameters),
CountVectorizer(parameters),
CountVectorizer(parameters),
CountVectorizer(parameters)
]
for v in vects:
print(v)
Whenever you see that variable names are being generated dynamically from strings, that's a warning that you need a better data structure to represent your data. Like a list, or a dictionary.
Of course not this, but try globals (in def use locals):
for i in range(1,5):
vect = globals()['vect_%s'%i]
print(vect)
Although still the most pythonic way is using #Oscar's solution
You can just loop through all your parameters.
vectors = []
parameters = []
#Put code for adding parameters here
for parameter in parameters:
vectors.append(CountVectorizer(parameter))
This loops through the parameters you have set and runs the function with each parameter. You can now access all the outputs from the vectors list.
I prefer using list or dict
def func_a(a):
print(a)
def func_b(b):
print(b, b)
def func_c(c):
print(c, c, c)
def func_d(d):
print(d, d, d, d)
# use list
func_list = [func_a, func_b, func_c, func_d]
for i in range(4):
func_list[i](i)
# use dict
func_dict = {"vect_1": func_a,
"vect_2": func_b,
"vect_3": func_c,
"vect_4": func_d}
for i in range(1, 5):
func_dict["vect_" + str(i)](i)
which will print
0
1 1
2 2 2
3 3 3 3
1
2 2
3 3 3
4 4 4 4

How to compare 4 consecutive elements in a list?

I am new to coding so I apologize in advance if what I am asking is simple or doesn't make much sense but I will try to elaborate as much as I can. First of all this is not for any work or project I am simply studying to learn a bit of coding for my satisfaction. I've been trying to find some real life problems to apply into coding (pseudo code mostly but python language would also be kind of understandable to me).
I wanted to be able to have a list of x elements and compare 4 of them sequentially.
For example, myList = [a, b, c, d, e, f, g, h, i, j, k, l]
First I want to compare a,b,c and d.
If b>a, c>b, d>c and d> all of 3 previous ones (d>a, d>b, d>c) I want to do something otherwise go to next comparison.
Then I wanted to compare b,c,d and e.
Similarly if c>b, d>c, e>d and e> all of 3 previous ones (e>b, e>c, e>d) I want to do something otherwise go to next comparison.
What if my list contains infinite elements? myList = [:]
Where do I start? Do I have to have a starting point?
I am guessing I have to use a for loop to iterate through the list but I honestly can't figure out how to iterate through the first 4 elements and then continue from the second element in 4 element batches.
Since I am currently studying the Arrays and lists maybe there is some functionality I am missing? Or I simply my brain can grasp it.
I tried looking at other posts in stackoverflow but honestly I can't figure it out from other people's answers. I would appreciate any help or guidance.
Thanks in advance.
You can use the built-in all() function for this problem:
myList = [5, 4, 3, 6, 3, 5, 6, 2, 3, 10, 11, 3]
def do_somthing():
#your code here
pass
for i in range(len(myList)-4):
new_list = myList[i:i+4] #here, using list slicing to jump ahead four elements.
if all(new_list[-1] > b for b in new_list[:-1]) and all(new_list[:-1][c] > new_list[:-1][c+1] for c in range(len(new_list)-2)):
do_something()
L = [...]
# get all the valid indices of the elements in the list, except for the last 4. These are the indices at which the 4-element windows start
for i in range(len(L)-4):
window = L[i:i+4] # the 4 elements you want to compare
print("I am considering the elements starting at index", i, ". They are:", window)
a,b,c,d = window
if d>a>b>c<d and d>b:
print("The checks pass!")
Now, there is a simpler way to do this:
for a,b,c,d in (L[i:i+4] for i in range(len(L)-4):
if d>a>b>c<d and d>b:
print("The checks pass!")
to consume just one item at a time from an iterator and operate on 4 lagged elements try a circle buffer:
# make a generator as example of 'infinte list'
import string
agen = (e for e in string.ascii_lowercase)
# initialize len 4 circle buffer
cb = [next(agen) for _ in range(4)] # assumes there are at least 4 items
ptr = 0 # initialize circle buffer pointer
while True:
a,b,c,d = (cb[(i+ptr)%4] for i in range(4)) # get current 4 saved items
# some fuction here
print(a,b,c,d)
# get next item from generator, catch StopIteration on empty
try:
cb[ptr] = next(agen)
except StopIteration:
break
ptr = (ptr + 1)%4 # update circle buffer pointer
a b c d
b c d e
c d e f
d e f g
e f g h
f g h i
g h i j
h i j k
i j k l
j k l m
k l m n
l m n o
m n o p
n o p q
o p q r
p q r s
q r s t
r s t u
s t u v
t u v w
u v w x
v w x y
w x y z
'some function' could include a stopping condition too:
# random.choice() as example of 'infinte iterator'
import string
import random
random.choice(string.ascii_lowercase)
# initialize len 4 circle buffer
cb = [random.choice(string.ascii_lowercase) for _ in range(4)] # assumes there are at least 4 items
ptr = 0 # initialize circile buffer pointer
while True:
a,b,c,d = (cb[(i+ptr)%4] for i in range(4)) # get current 4 saved items
# some fuction here
print(a,b,c,d)
if a<b<c<d: # stopping condition
print("found ordered string: ", a,b,c,d)
break
# get next item from generator, catch StopIteration on empty
try:
cb[ptr] = random.choice(string.ascii_lowercase)
except StopIteration:
break
ptr = (ptr + 1)%4 # update circle buffer pointer
o s w q
s w q k
w q k j
q k j r
k j r q
j r q r
r q r u
q r u v
found ordered string: q r u v
Since you can index a list, how about start from index 0, compare the 0th, (0+1)th, (0+2)th, and (0+3)th elements. Then, by the next round, increase your index to 1, and compare the 1st, (1+1)th, (1+2)th, and (1+3)th elements, and so on. For the nth round, you compare the n, n+1, n+2, and (n+3)th elements, until you reach the 4th element before the end. This is how you generally do stuff like 'testing m elements each time from a sequence of length n', and you can easily expand this pattern to matrices or 3d arrays. The code you see in other answers are basically all doing this, and certain features in Python make this job very easy.
Now, 'what if the list contains infinite elements'? Well, then you'll need a generator, which is a bit advanced at this stage I assume, but the concept is very simple: you let a function read that infinite stream of elements in a (might be infinite) loop, set a cursor on one of them, return (yield) the element under the cursor as well as the 3 elements following it each time, and increase the cursor by one before the next loop starts:
def unsc_infinity(somelist):
cur = 0
while True:
yield somelist[c:c+4]
cur = cur + 1
infinity_reader = unsc_infinity(endless_stream)
next(infinity_reader)
# gives the 0, 1, 2, 3 th elements in endless_stream
next(infinity_reader)
# gives the 1, 2, 3, 4 th elements in endless_stream
next(infinity_reader)
# ...
And you can loop over that generator too:
for a, b, c, d in unsc_infinity(endless_stream):
if d>a>b>c<d and d>b:
do_something()
Hope that helps a bit for you to build a mental model about how this kind of problems are done.

Storing every value of a changing variable

I am writing a piece of code that takes an input that varies according to discrete time steps. For each time step, I get a new value for the input.
How can I store each value as a list?
Here's an example:
"""when t = 0, d = a
when t = 1, d = b
when t = 2, d = c"""
n = []
n.append(d) #d is the changing variable
for i in range(t):
n.append(d)
What I expect to get is:
for t = 0, n = [a]; for t = 1, n = [a,b]; and for t = 2, n = [a,b,c]
What I actually get is:
for t = 0, n = [a], for t = 1, n = [b,b]; and for t = 2, n = [c,c,c]
See comment below, but based on the additional info you've provided, replace this:
n.append(d)
with this:
n.append(d[:])
Which type is the variable 'd'? If it is, for instance a list, the code you are showing pushes onto tbe list 'n' a reference to the variable 'd' rather than a copy of it. Thus, for each iteration of the loop you add a new reference of 'd' (like a pointer in C) to 'n', and when 'd' is updated all the entries in 'n' have, of course, the same value
To fix it you can modify the code so as to append a copy of 'd', either:
n.append(d[:])
n.append(list(d))
n.append(tuple(d))
You can simply do this
n = []
for i in range(t + 1):
n.append(chr(i+ord('a'))
And if you do not want to store the characters in the list rather some specific values which are related with d, then you have to change d in the for loop
n = []
d = 1
for i in range(t + 1):
n.append(d)
d += 2
It is difficult to say without seeing the code. But if d is not an int, this could happen. If d is a list for instance, it is passed by reference
n = []
d = [1]
n.append(d)
d[0] = 2
n.append(d)
print(n)
>>>> [[2], [2]]
So if each new d is just modified, your probleme arise. You can solve it by copying d :
from copy import copy
n = []
d = [1]
n.append(copy(d))
d[0] = 2
n.append(copy(d))
print(n)
>>>> [[1], [2]]
If you just wrap the variable inside an object you can watch what is being set to the variable by overriding __setattr__ method. A simple example.
class DummyClass(object):
def __init__(self, x):
self.history_of_x=[]
self.x = x
self._locked = True
def __setattr__(self, name, value):
self.__dict__[name] = value
if name == "x":
self.history_of_x.append(value)
d = DummyClass(4)
d.x=0
d.x=2
d.x=3
d.x=45
print d.history_of_x
Output :-
[4, 0, 2, 3, 45]

Categories

Resources