Storing every value of a changing variable - python

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]

Related

python swaping values using comma is causing confusions

this involves a problem that I encountered when try to solve a linked-list reverse problem.
First let me put some preliminary codes for the definition of the linked list and quick method to generate a linked list:
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
def __repr__(self):
if self.next:
return "{}->{}".format(self.val, repr(self.next))
else:
return "{}".format(self.val)
def genNode(*nodes, end=None):
if len(nodes) == 1 and type(nodes[0]) == list:
nodes = nodes[0]
for i in nodes[::-1]:
n = ListNode(i)
n.next, end = end, n
return n if nodes else None
The problem I have is that I found the swapping mechanism is still depending on the sequence of the variable that I write.
Originally when we talk about swapping values in python we can do:
a, b = b, a
and it should work the same way if I have
b, a = a, b
This reverse linked list method that I am trying to write has 3 variables swapping, the idea is simple, to create a dummy head, and consistently adding nodes between dummy and dummy.next, so that it can be reversed.
def rev(head):
dummy = ListNode('X')
while head:
dummy.next, head.next, head = head, dummy.next, head.next
return dummy.next
a = genNode(1,2,3,4)
print(rev(a)) # >>> 4->3->2->1
But If I slightly switch the sequence of the 3 variables:
def rev2(head):
dummy = ListNode('X')
while head:
dummy.next, head, head.next, = head, head.next, dummy.next,
return dummy.next
a = genNode(1,2,3,4)
print(rev2(a)) # >>> AttributeError: 'NoneType' object has no attribute 'next'
So it does seem like that the sequence matters here, and can anyone let me know how python evaluate swapping values if there is more than 2 variables.
Thanks!
left to right
Look at https://docs.python.org/3/reference/simple_stmts.html#assignment-statements
CPython implementation detail: In the current implementation, the syntax for targets is taken to be the same as for expressions, and invalid syntax is rejected during the code generation phase, causing less detailed error messages.
Although the definition of assignment implies that overlaps between the left-hand side and the right-hand side are ‘simultaneous’ (for example a, b = b, a swaps two variables), overlaps within the collection of assigned-to variables occur left-to-right, sometimes resulting in confusion. For instance, the following program prints [0, 2]:
x = [0, 1]
i = 0
i, x[i] = 1, 2 # i is updated, then x[i] is updated
print(x)
A simple example below should show you the caveat of using swapping for a class like ListNode
Let's define a 3 element linked list.
a = ListNode(1)
b = ListNode(2)
c = ListNode(3)
a.next = b
b.next = c
print(a)
#1->2->3
Now if we swap say b and c, it won't have any effect
b,c = c,b
print(a)
#1->2->3
If we swap a and b, the linked list changes.
a,b=b,a
print(a)
#2->3
Similarly for a and c swap.
a,c=c,a
print(a)
#3
So you can see that using the simple swap logic is inconsistent in how it applies to a ListNode, hence should be avoided.
Interesting discussion, kind of extending the answer above, I created this new example below.
x = [1, 0]
i = 0
i, x[i] = x[i], i
print(i, x)
>> 1 [1, 0]
Let's go through step by step to see what is going one with i, x[i] = x[i], i.
Initially, all variables are at the previous stage, i.e., i=0, so x[i] is x[0]=1 for both sides. We have 0, x[0] = x[0], 0 or 0, 1 = 1, 0
The exchange/assignment starts from left to right. The left part of comma,i = x[i] takes place first, that is i = 1, i value changes from 0 to 1.
Importantly, when the right part of comma takes place, the value of i already changed. We are actually looking at, 1, x[1] = 1, 0, the confusing part is that the i on the right will not change and its value is still 0, not the new value, 1, x[1] = 1, i. Thus, the final state is, 1, x[1] = 1, 0.
If there are more than two variables it works identically as with two. You put them in the desired final order:
>>> a = 1
>>> b = 2
>>> c = 3
>>> c,b,a = a,b,c
>>> a,b,c
(3, 2, 1)

Python: print variable name and value easily

I want to use a function that can automatically print out the variable and the value. Like shown below:
num = 3
coolprint(num)
output:
num = 3
furthermore, it would be cool if it could also do something like this:
variable.a = 3
variable.b = 5
coolprint(vars(variable))
output:
vars(variable) = {'a': 3, 'b': 5}
Is there any function like this already out there? Or should I make my own? Thanks
From Python 3.8 there is a = for f-strings:
#!/usr/bin/env python3
python="rocks"
print(f"{python=}")
This would output
# python=rocks
This lambda-based solution works well enough for me, though perhaps not in every case. It is very simple and only consumes one line.
coolprint = lambda *w: [print(x,'=',eval(x)) for x in w]
Exmaple..
coolprint = lambda *w: [print(x,'=',eval(x)) for x in w]
a, *b, c = [1, 2, 3, 4, 5]
coolprint('a')
coolprint('b','c')
coolprint('a','b','c')
coolprint('c','b','b','a','b','c')
which produces..
a = 1
b = [2, 3, 4]
c = 5
a = 1
b = [2, 3, 4]
c = 5
a = 1
b = [2, 3, 4]
c = 5
c = 5
b = [2, 3, 4]
b = [2, 3, 4]
a = 1
b = [2, 3, 4]
c = 5
An official way to accomplish this task sadly doesn't exist even though it could be useful for many people. What I would suggest and I have used it sometimes in the past is the following(I am not sure if this is the best way to do it).
Basically what you can do is to create a custom object that mimics one Python's data type per time. Bellow you can find an example for an integer.
class CustomVariable(object):
def __init__(self, name, value):
self.name = name
self.value = value
def __str__(self):
return "{} = {}".format(self.name, self.value)
def __add__(self, val) :
return self.value + val
myVar = CustomVariable("myVar", 15)
print myVar
myVar = myVar + 5
print myVar
Output:
myVar = 15
myVar = 20
Check the special method named "___str____"
I'm just appending my custom print which accepts a list or a dict of expressions, adding the globals at the lateral case (excuse the PEP violation, I just love single-liners, especially with lambdas):
pp = lambda p: print(' | '.join([f"{x} = {eval(x)}" for x, v in {**p, **dict(**globals())}.items() if not x.startswith('_')])) if type(p)==dict else print(' | '.join([f"{x} = {eval(x)}" for x in p if not x.startswith('_')]))
# Some Variables:
z = {'a':100, 'b':200}
a = 1+15
# Usage with dict & all the variables in the script:
pp(dict(**locals()))
# Usage with list, for specific expressions:
pp(['a', "z['b']", """ {x:x+a for x in range(95, z['a'])} """])
Note that f'{x =}' won't work correctly, since the evaluation takes scope inside the function.
I have discovered the answer is No. There is no way to do this. However, your best bet is something like this:
from pprint import pprint
def crint(obj, name):
if isinstance(obj, dict):
print '\n' + name + ' = '
pprint(obj)
else:
print '\n' + name + ' = ' + str(obj)
that way you can just do:
crint(vars(table.content[0]), 'vars(table.content[0])')
or:
j = 3
crint(j, 'j')

How do I generate a table from a list

I have a list that contains sublists with 3 values and I need to print out a list that looks like:
I also need to compare the third column values with eachother to tell if they are increasing or decreasing as you go down.
bb = 3.9
lowest = 0.4
#appending all the information to a list
allinfo= []
while bb>=lowest:
everything = angleWithPost(bb,cc,dd,ee)
allinfo.append(everything)
bb-=0.1
I think the general idea for finding out whether or not the third column values are increasing or decreasing is:
#Checking whether or not Fnet are increasing or decreasing
ii=0
while ii<=(10*(bb-lowest)):
if allinfo[ii][2]>allinfo[ii+1][2]:
abc = "decreasing"
elif allinfo[ii][2]<allinfo[ii+1][2]:
abc = "increasing"
ii+=1
Then when i want to print out my table similar to the one above.
jj=0
while jj<=(10*(bb-lowest))
print "%8.2f %12.2f %12.2f %s" %(allinfo[jj][0], allinfo[jj][1], allinfo[jj][2], abc)
jj+=1
here is the angle with part
def chainPoints(aa,DIS,SEG,H):
#xtuple x chain points
n=0
xterms = []
xterm = -DIS
while n<=SEG:
xterms.append(xterm)
n+=1
xterm = -DIS + n*2*DIS/(SEG)
#
#ytuple y chain points
k=0
yterms = []
while k<=SEG:
yterm = H + aa*m.cosh(xterms[k]/aa) - aa*m.cosh(DIS/aa)
yterms.append(yterm)
k+=1
return(xterms,yterms)
#
#
def chainLength(aa,DIS,SEG,H):
xterms, yterms = chainPoints(aa,DIS,SEG,H)# using x points and y points from the chainpoints function
#length of chain
ff=1
Lterm=0.
totallength=0.
while ff<=SEG:
Lterm = m.sqrt((xterms[ff]-xterms[ff-1])**2 + (yterms[ff]-yterms[ff-1])**2)
totallength += Lterm
ff+=1
return(totallength)
#
def angleWithPost(aa,DIS,SEG,H):
xterms, yterms = chainPoints(aa,DIS,SEG,H)
totallength = chainLength(aa,DIS,SEG,H)
#Find the angle
thetaradians = (m.pi)/2. + m.atan(((yterms[1]-yterms[0])/(xterms[1]-xterms[0])))
#Need to print out the degrees
thetadegrees = (180/m.pi)*thetaradians
#finding the net force
Fnet = abs((rho*grav*totallength))/(2.*m.cos(thetaradians))
return(totallength, thetadegrees, Fnet)
Review this Python2 implementation which uses map and an iterator trick.
from itertools import izip_longest, islice
from pprint import pprint
data = [
[1, 2, 3],
[1, 2, 4],
[1, 2, 3],
[1, 2, 5],
]
class AddDirection(object):
def __init__(self):
# This default is used if the series begins with equal values or has a
# single element.
self.increasing = True
def __call__(self, pair):
crow, nrow = pair
if nrow is None or crow[-1] == nrow[-1]:
# This is the last row or the direction didn't change. Just return
# the direction we previouly had.
inc = self.increasing
elif crow[-1] > nrow[-1]:
inc = False
else:
# Here crow[-1] < nrow[-1].
inc = True
self.increasing = inc
return crow + ["Increasing" if inc else "Decreasing"]
result = map(AddDirection(), izip_longest(data, islice(data, 1, None)))
pprint(result)
The output:
pts/1$ python2 a.py
[[1, 2, 3, 'Increasing'],
[1, 2, 4, 'Decreasing'],
[1, 2, 3, 'Increasing'],
[1, 2, 5, 'Increasing']]
Whenever you want to transform the contents of a list (in this case the list of rows), map is a good place where to begin thinking.
When the algorithm requires data from several places of a list, offsetting the list and zipping the needed values is also a powerful technique. Using generators so that the list doesn't have to be copied, makes this viable in real code.
Finally, when you need to keep state between calls (in this case the direction), using an object is the best choice.
Sorry if the code is too terse!
Basically you want to add a 4th column to the inner list and print the results?
#print headers of table here, use .format for consistent padding
previous = 0
for l in outer_list:
if l[2] > previous:
l.append('increasing')
elif l[2] < previous:
l.append('decreasing')
previous = l[2]
#print row here use .format for consistent padding
Update for list of tuples, add value to tuple:
import random
outer_list = [ (i, i, random.randint(0,10),)for i in range(0,10)]
previous = 0
allinfo = []
for l in outer_list:
if l[2] > previous:
allinfo.append(l +('increasing',))
elif l[2] < previous:
allinfo.append(l +('decreasing',))
previous = l[2]
#print row here use .format for consistent padding
print(allinfo)
This most definitely can be optimized and you could reduce the number of times you are iterating over the data.

Python QuickSort returning original input

So here is my quicksort code
def quicksort(A):
if len(A) > 1:
pivot = A[0]
L = []
E = []
R = []
for i in A:
if i < pivot:
L.append(i)
elif i == pivot:
E.append(i)
else:
R.append(i)
quicksort(L)
quicksort(R)
A = L + E + R
And the output when I run
array = [5,6,3,2,7]
print "original array" + str(array)
quicksort(array)
print "sorted array" + str(array)
Is
original array[5, 6, 3, 2, 7]
sorted array[5, 6, 3, 2, 7]
However, when I step through the function with the debugger, the last value A ever holds is [2,3,5,6,7] which is sorted, why does A not hold this after the function is executed?
You build a new list A = L + E + R, rebinding the local name. The original list is unaffected.
You need to return the new list objects:
def quicksort(A):
if len(A) > 1:
pivot = A[0]
L = []
E = []
R = []
for i in A:
if i < pivot:
L.append(i)
elif i == pivot:
E.append(i)
else:
R.append(i)
L = quicksort(L)
R = quicksort(R)
return L + E + R
return A
This returns a new list object:
>>> quicksort([5,6,3,2,7])
[2, 3, 5, 6, 7]
Alternatively, update the list in-place by using slice assignment:
def quicksort(A):
if len(A) > 1:
pivot = A[0]
L = []
E = []
R = []
for i in A:
if i < pivot:
L.append(i)
elif i == pivot:
E.append(i)
else:
R.append(i)
quicksort(L)
quicksort(R)
A[:] = L + E + R
Now the original list is altered in-place:
>>> lst = [5,6,3,2,7]
>>> quicksort(lst)
>>> lst
[2, 3, 5, 6, 7]
The problem is that the last line doesn't do what you seem to think it does:
A = L + E + R
This adds L, E, and R together into a new list, and binds that new list to the local variable A. It doesn't affect the list that A used to be bound to in any way.
What you probably wanted to do was replace the contents of the existing list that A is bound to. Like this:
A[:] = L + E + R
You ask "why does A not hold this after the function is executed?" Well, after the function is executed, A doesn't exist at all. That's the whole point of a local variable.
Of course array exists. And array and A are originally references to the same list. If you mutate that list, you get what you want. If you make A refer to a different list, you don't.

Efficient way of counting True and False

This may be a trivial problem, but I want to learn more about other more clever and efficient ways of solving it.
I have a list of items and each item has a property a whose value is binary.
If every item in the list has a == 0, then I set a separate variable b = 0.
If every item in the list has a == 1, then I set b = 1.
If there is a mixture of a == 0 and a == 1 in the list, then I set
b = 2.
I can use a set to keep track of the types of a value, such that if there are two items in the set after iterating through the list, then I can set b = 2, whereas if there is only one item in the set I just retrieve the item (either 0 or 1) and use it to set b.
Any better way?
One pass through the list, and no extra data structures constructed:
def zot(bs):
n, s = len(bs), sum(bs)
return 1 if n == s else 2 if s else 0
I would suggest using any and all. I would say that the benefit of this is readability rather than cleverness or efficiency. For example:
>>> vals0 = [0, 0, 0, 0, 0]
>>> vals1 = [1, 1, 1, 1, 1]
>>> vals2 = [0, 1, 0, 1, 0]
>>> def category(vals):
... if all(vals):
... return 1
... elif any(vals):
... return 2
... else:
... return 0
...
>>> category(vals0)
0
>>> category(vals1)
1
>>> category(vals2)
2
This can be shortened a bit if you like:
>>> def category(vals):
... return 1 if all(vals) else 2 if any(vals) else 0
...
This works with anything that can be interpreted by __nonzero__ (or __bool__ in Python 3) as having a true or false value.
Somebody mentioned code golf, so can't resist a variation on #senderle's:
[0,2,1][all(vals) + any(vals)]
Short explanation: This uses the boolean values as their integer equivalents to index a list of desired responses. If all is true then any must also be true, so their sum is 2. any by itself gives 1 and no matches gives 0. These indices return the corresponding values from the list.
If the original requirements could be modified to use 1 for any and 2 for all it would be even simpler to just return the integer of any + all
Using a dictionary:
zonk_values = {frozenset([0]): 0, frozenset([1]): 1, frozenset([0, 1]): 2}
def zonk(a):
return zonk_values[frozenset(a)]
This also only needs a single pass through the list.
you could also use sets.
s = set([i.a for i in your_list])
if len(s) == 1:
b = s.pop()
else:
b = 2
def zot(bs):
return len(set(bs)) if sum(bs) else 0
You can define two boolean vars hasZero and hasOne and set them to True if corresponding value was met while iterating the list. Then b = 2 if hasZero and hasOne, b = 1 if only hasOne and b = 0 if only hasZero.
Another way: you can sum all the a values along the list. If sumA == len(list) then b = 1, if sumA == 0 then b = 0 and if 0 < sumA < len(list) then b = 2.
Short-circuiting solution. Probably the most efficient way you can do it in Python.
EDIT: Included any and all as per suggestion in comments.
EDIT2: It's now a one-liner.
b = 1 if all(A) else 2 if any(A) else 0
This is similar to senderle's suggestion, but written to access the objects' a properties.
from random import randint
class Item(object):
def __init__(self, a):
self.a = a
all_zeros = [Item(0) for _ in xrange(10)]
all_ones = [Item(1) for _ in xrange(10)]
mixture = [Item(randint(0, 1)) for _ in xrange(10)]
def check(items):
if all(item.a for item in items):
return 1
if any(item.a for item in items):
return 2
else:
return 0
print 'check(all_zeros):', check(all_zeros)
print 'check(all_ones):', check(all_ones)
print 'check(mixture):', check(mixture)
You can use list iterators:
>>> L = [0, 0, 0, 0, 0]
>>> L1 = [1, 1, 1, 1, 1]
>>> L2 = [0, 1, 0, 1, 0]
>>> def fn(i):
... i = iter(i)
... if all(i): return 1
... return 2 if any(i) else 0
...
>>> fn(L)
0
>>> fn(L1)
1
>>> fn(L2)
2

Categories

Resources