Python list in list undo - python

I'm making a program that will do some operations like add, insert, pop on a list of numbers.
I have to also make an "undo" function and for that I thought about making a list in list to save up my list every step of the way but my tests are not working. Here's the code:
def backup(l):
lbackup.append(list(l))
<-- here I save the list every step of the way by calling this function
> def undo(l):
> l=lbackup[len(lbackup)-1] # here I give my list the previous list
> del lbackup[len(lbackup)-1] # here I delete the sublist from the history
My test:
def testUndo():
add(lista, 1)
backup(lista)
add(lista, 2)
backup(lista)
add(lista,111111)
print(lbackup)
print(lista)
undo(lista)
print(lista)
print(lbackup)
So I checked the inside of the Undo function and it's working! It actually changed the value of my list to what I wanted but once it left the function and got to the print after undo in the test... it had the same values again. I know lists are mutable and that might be a problem but is there anything I could do? Sorry if I wasnt clean enough.

When you call, say, undo(lista), the l inside of undo may be the same list as lista, but assigning to l does not change lista. If instead you had undo return l, and assign the value returned by undo back to list, that would make the change you seem to want.

Related

How do I code a proxy for the .pop method without actually using the .pop method?

I am attempting (well I have solved it, just not the "right" way) to solve the following problem:
A Stack is a type of list where instead of accessing
any item in the list at any time, you can only add
or remove items from the top of the Stack.
Adding a new item to the stack is called "pushing"
the item onto the stack. Removing the top item on
the stack is called "popping" the item off the stack.
When an item is "popped" off the stack, it is removed
from the list altogether.
Write a class called Stack. Stack should have the
following methods:
An init method that initializes the empty list
that is the stack's contents.
A stack_push() method that takes one parameter (in
addition to self): an item to push onto the top
of the stack.
A stack_pop() method that returns the current top
item on the stack and removes it from the underlying
list. If the list is already empty, this returns
None.
For example, the following code would print the
numbers 3, 2, and 1 (in that order). Note that this
is the opposite order of how they are pushed: the
pop method will always return the elements in the
reverse order that they were added in.
new_stack = Stack()
new_stack.stack_push(1)
new_stack.stack_push(2)
new_stack.stack_push(3)
print(new_stack.stack_pop())
print(new_stack.stack_pop())
print(new_stack.stack_pop())
Add your class here!
Here is what I came up with:
class Stack:
def __init__(self):
self.a_list =[]
def stack_push(self,num):
self.a_list.append(num)
def stack_pop(self):
return self.a_list.pop()
This works, but I am not allowed to use the .pop method in the last line. Any thoughts? Here is the code that test my class:
The following lines of code will test your class.
If it works correctly, it will print 3, 2, and 1
in that order, each on their own line.
new_stack = Stack()
new_stack.stack_push(1)
new_stack.stack_push(2)
new_stack.stack_push(3)
print(new_stack.stack_pop())
print(new_stack.stack_pop())
print(new_stack.stack_pop())
My code DOES print 3,2,1 in this order, each integer on a separate line. Again though, I am not supposed to use .pop which I didn't realize until after I solved this. Plus, I am basically a beginner and was introduced to the .pop method in this question.
One possible way to implement a stack would be to emulate how the actual CPU stack works on your computer:
allocate a very long list for the stack
have an integer variable which contains the last used position in the stack, let's call it SP.
push increases SP and puts the value in stack[SP]
pop returns stack[SP] and decreases SP
Note that you also need to handle stack under- and overflows (which your original code is missing either).
The last element appended to the list can be retrieved with
last_el = self.a_list[-1]
but then you still need to remove it from the list to 'pop' it out,
so you could then do
self.a_list = self.a_list[:-1]
all together you get:
def stack_pop(self):
last_el = self.a_list[-1]
self.a_list = self.a_list[:-1]
return last_el
but this is probably less efficient than using .pop() anyway.

Python shallow copy and deep copy in using append method

Some problems come out when using append method in python3.5. The code is presented
# generate boson basis in lexicographic order
def boson_basis(L,N):
basis=[]
state=[0 for i in range(1,L+1)]
pos=0
# initialize the state to |N,0,...,0>
state[0]=N
basis.append(state)
# find the first non-zero position in reverse order
while state[L-1]<N:
for i in range(-2,-L-1,-1):
if state[i]>0:
pos=L+i
break
sum=0
for i in range(0,pos):
sum=sum+state[i]
state[pos]=state[pos]-1
state[pos+1]=N-sum-state[pos]
basis.append(state)
return basis
result=boson_basis(3,3)
the expected result should be [[3,0,0],[2,1,0],...,[0,0,3]], but this code generates wrong results with all elements are the same as the last one, i.e. [[0,0,3],...,[0,0,3]]. I use the pdb to debug it and I find that once the state is modified, the former state that has been appended into basis is also changed simultaneously. It implies that append uses deepcopy automatically which is beyond my understanding. In fact, this error can be fixed if we use basis(state.copy()) explicitly.
On the other hand, the following simple code shows no error in using append
x=3
b=[]
b.append(x)
x=x+2
after x is changed to x=5, b remains unchanged b=[3]. It really puzzles me and seems contradictory with the former example.
As revealed in the comments already, there's no copy whatsoever involved in an append operation.
So you'll have to explicitly take care of this yourself, e.g. by replacing
basis.append(state)
with
basis.append(state[:])
The slicing operation with : creates a copy of state.
Mind: it does not copy the lists elements - which as long as you're keeping only plain numbers and not objects in your list should be fine though.

list.append(variable) doesn't append but replaces the list

I have created a function which creates an empty list, and assigns a value from another list to a variable. Then the code is supposed to append the variable's value to the list each time the function is called. But the function doesn't append, but instead replaces the element inside the list. I can tell this because when I print the list, the previous element is missing. The code is below. I need this for a mock examination. Any ideas?
def track():
global tracker
global trackerresult
trackerresult = []
tracker = opposite1[decider]
trackerresult.append(tracker)
print(trackerresult)
It's not the append that is doing that, but your explicit replacement of the existing variable two lines earlier.
As CactusWoman points out, you don't need globals here at all. tracker is a purely local variable; and as for trackerresult, once you've got rid of the assignment you'll only be mutating it, so there is no need to declare it as global.
Every single time out run the track function, it resets the value
trackerresult to the empty list. If you didn't explicitly empty the list, instead doing trackerresult=[] once before the function definition, it would work.

Why is my (local) variable behaving like a global variable?

I have no use for a global variable and never define one explicitly, and yet I seem to have one in my code. Can you help me make it local, please?
def algo(X): # randomized algorithm
while len(X)>2:
# do a bunch of things to nested list X
print(X)
# tracing: output is the same every time, where it shouldn't be.
return len(X[1][1])
def find_min(X): # iterate algo() multiple times to find minimum
m = float('inf')
for i in some_range:
new = algo(X)
m = min(m, new)
return m
X = [[[..], [...]],
[[..], [...]],
[[..], [...]]]
print(find_min(X))
print(X)
# same value as inside the algo() call, even though it shouldn't be affected.
X appears to be behaving like a global variable. The randomized algorithm algo() is really performed only once on the first call because with X retaining its changed value, it never makes it inside the while loop. The purpose of iterations in find_min is thus defeated.
I'm new to python and even newer to this forum, so let me know if I need to clarify my question. Thanks.
update Many thanks for all the answers so far. I almost understand it, except I've done something like this before with a happier result. Could you explain why this code below is different, please?
def qsort(X):
for ...
# recursively sort X in place
count+=1 # count number of operations
return X, count
X = [ , , , ]
Y, count = qsort(X)
print(Y) # sorted
print(X) # original, unsorted.
Thank you.
update II To answer my own second question, the difference seems to be the use of a list method in the first code (not shown) and the lack thereof in the second code.
As others have pointed out already, the problem is that the list is passed as a reference to the function, so the list inside the function body is the very same object as the one you passed to it as an argument. Any mutations your function performs are thus visible from outside.
To solve this, your algo function should operate on a copy of the list that it gets passed.
As you're operating on a nested list, you should use the deepcopy function from the copy module to create a copy of your list that you can freely mutate without affecting anything outside of your function. The built-in list function can also be used to copy lists, but it only creates shallow copies, which isn't what you want for nested lists, because the inner lists would still just be pointers to the same objects.
from copy import deepcopy
def algo (X):
X = deepcopy(X)
...
When you do find_min(X), you are passing the object X (a list in this case) to the function. If that function mutates the list (e.g., by appending to it) then yes, it will affect the original object. Python does not copy objects just because you pass them to a function.
When you pass an object to a python function, the object isn't copied, but rather a pointer to the object is passed.
This makes sense because it greatly speeds up execution - in the case of a long list, there is no need to copy all of its elements.
However, this means that when you modify a passed object (for example, your list X), the modification applies to that object, even after the function returns.
For example:
def foo(x):
x.extend('a')
print x
l = []
foo(l)
foo(l)
Will print:
['a']
['a', 'a']
Python lists are mutable (i.e., they can be changed) and the use of algo within find_min function call does change the value of X (i.e., it is pass-by-reference for lists). See this SO question, for example.

Can't Delete Function Call

This question is just out of general curiosity. I've just noticed it when working on my current project (surprisingly I haven't came across before today).
Take this code:
List = ["hi","stack","over","flow","how","you","doing"]
del List(len(List)-1)
Error:
SyntaxError: can't delete function call
I don't understand why you aren't allowed to delete an index of a list by referencing a call to a function? Do I just shut up and accept you can't do it or am I doing something fundamentally wrong?
I apologise if there is an easy answer to this but either Google is getting less helpful or this is so blatantly obvious I need help.
You meant to delete the last element of the list, not somehow call List as a function:
del List[len(List)-1]
Python's del statement must take specific forms like deleting a variable, list[element], or object.property. These forms can be nested deeply, but must be followed. It parallels the assignment statement -- you'll get a similar syntax error if you try to assign to a function call.
Of course, what you really want in this case is
del List[-1]
which means the last element of the list, and is way more Pythonic.
You are calling a function List() when you should be indexing a list, List[].
In Python, Round parenthesis, (), are used to call functions, while square brackets, [] are used to index lists and other sequences.
Try:
del List[len(List) - 1]
or even better, use the fact that Python allows negative indexes, which count from the end:
del List[-1]
Also, you might want to make the list's name not so close to the built-in list type name, for clarity.
You are allowed. However, you are using the wrong syntax. Correct syntax is:
del List[-1]
Notice that the "len(List) part is useless.

Categories

Resources