How to modify a variable in a class function - python

So i got this function. It must not be changed!
class TestUnikati(unittest.TestCase):
def test_02_clean(self):
s = [5, 4, 1, 4, 4, 4, 4, 4, 3]
unique_array(s) #<-- calls the function
self.assertEqual(s, [5, 4, 1, 3])
Basically we test if the function clean() only returns a unique array. The variable s being the array.
This is my function that get's the messy array s and tries to return an array of no duplicate elements
def unique_array(s):
s=unique(s) #<-- this is a function that just returns a unique array
x=TestUnikati() #<-- I store the class in the x variable
[insert a way to push s to the "TestUnikati.test_02_clean.s"]
I tried many things. I've tried some experiments with globals() and locals() as well as many things with the x object variable but I don't seem to get it right.
I've tried to push it to the locals() of TestUnikati.test_02_clean.s with the x object. Is there a way to save it so the s in the class function will be over-ridden and the self.assertEqual(s, [5, 4, 1, 3]) will compare the 2 and pass it? Something like this:
x.test_02_clean.s=unique(s)
or
x.s=unique(s)

As others have stated, since test_02_clean() doesn't assign the results of its call of unique_array() to anything, it's expecting you to mutate the given list in place. Something like this:
def unique_array(s):
# Keep a set of all the items we've seen so far
seen = set()
# Index into list
i = 0
while i < len(s):
if s[i] in seen:
# Delete element from list if we've already seen it
del s[i]
else:
# Or else add it to our seen list and increment the index
seen.add(s[i])
i += 1
>>> s = [5, 4, 1, 4, 4, 4, 4, 4, 3]
>>> unique_array(s)
>>> s
[5, 4, 1, 3]

you must have to change your class variable to access globally like below:-
class TestUnikati(unittest.TestCase):
def test_02_clean(self):
self.s = [5, 4, 1, 4, 4, 4, 4, 4, 3]
unique(self.s) <-- calls the function
self.assertEqual(self.s, [5, 4, 1, 3])
and also to reassign it with shorted value you have to do like:-
self.s=unique(s)

You need a function unique() that changes the list in place, instead of returning a new list, e.g.
def unique(l):
count = {}
for entry in l:
count[entry] = count.get(entry, 0) + 1
for entry in l:
for _ in range(count[entry] - 1):
l.remove(entry)
This works in place:
a = [1, 1, 1, 3, 4, 2]
unique(a)
print(a)
>>> [1, 3, 4, 2]

Related

inserted list changes even after being inserted

I wanted to create a list of lists
def func(m,n,l,t):
a=[i for i in range(m)]
b=[]
b.append(a)
a=swap(a)
b.append(a)
for i in b:
print(i)
def swap(l):
i,n=0,len(l)
while(i<n):
l[i],l[i+1]=l[i+1],l[i]
i+=2
return l
i created list a as my basis and append each modification i want to in the b list.
The problem is after i append the first list and modify it, the first one i inserted also changes the same as the second one i inserted.
the output of this code is this
[1, 0, 3, 2, 5, 4, 7, 6]
[1, 0, 3, 2, 5, 4, 7, 6]
what i want is when i performm the swap i dont want the first list to change
the output should look like this
[0, 1, 2, 3, 4, 5, 6, 7]
[1, 0, 3, 2, 5, 4, 7, 6]
if you could help, please thank you.
In place of append() method, you can try out extend() method which does the same as of append. but extend() method is mainly used for iterate items, as of in your case.
and also if you want to use only append, then take a copy of your variable first and append it, as you are changing in the first place, it is taking effect in the list too. So you can follow append into a list like, a.copy(). Hopes it helps
Please try out and share the feedback.
Thank you
Their are multiple code error you have done, for example:
you don't have to iterate over range object in order to get list, you could just...
a = list(range(m))
and also You don't have to run a while loop in order to iterate over two steps you could...
for i in range(0, len(l), 2):
see range for reference
also you didn't use any of n, l, t parameters of the func function.
THE ANSWER
when you pass the a variable to the swap function you are actually passing the class Object itself, so the swap function can change the value of the a variable (list class).
All you need is passing a copy of the a variable not the variable itself
a = swap(a.copy())
FINAL CODE
def swap(l):
for i in range(0, len(l), 2):
l[i],l[i+1]=l[i+1],l[i]
return l
def func(m):
a = list(range(m))
b = [a]
a = swap(a.copy())
b.append(a)
for i in b:
print(i)
func(8) # [0, 1, 2, 3, 4, 5, 6, 7], [1, 0, 3, 2, 5, 4, 7, 6]

How to insert an element in a list after using del in python

def my_len(lst):
mylen = 0
for i in lst:
mylen += 1
return mylen
def insrt(lst, what, where):
length = my_len(lst)
tmp = [0] * (length + 1)
for i in range(0, where):
tmp[i] = lst[i]
tmp[where] = what
for i in range(where, length + 1):
tmp[i] = lst[i - 1]
return tmp
def move(lst, from_index, to_index):
a = lst[from_index]
del lst[from_index]
insrt(lst, a, to_index)
return lst
my intention was if the original list input is [1, 2, 3, 4, 5, 6], then print(move(lst, 3, 1) will give [1, 4, 2, 3, 5, 6], but my code gives [1, 2, 3, 5, 6]. Please explain how to fix it.
NB. See the end of the answer for an alternative interpretation of the move
If you want the item to end up in the defined to_index, you can simply use list.insert and list.pop:
def move(lst, from_index, to_index):
lst.insert(to_index, lst.pop(from_index))
examples:
l = [1, 2, 3, 4, 5, 6]
move(l, 1, 3)
print(l)
[1, 3, 4, 2, 5, 6]
l = [1, 2, 3, 4, 5, 6]
move(l, 3, 1)
print(l)
[1, 4, 2, 3, 5, 6]
l = [1, 2, 3, 4, 5, 6]
move(l, 3, 3)
print(l)
[1, 2, 3, 4, 5, 6]
NB. your function should probably either return a copy, or modify the list in place, not both. (here I chose to modify in place, see below for a variant).
copy variant:
def move(lst, from_index, to_index):
lst = lst.copy()
lst.insert(to_index, lst.pop(from_index))
return lst
l = [1, 2, 3, 4, 5, 6]
l2 = move(l, 1, 3)
print(l)
[1, 2, 3, 4, 5, 6]
print(l2)
[1, 3, 4, 2, 5, 6]
alternative interpretation: move to the position as defined before the move
In this different interpretation, the item moves to the position as defined if the insertion occured before the deletion.
In this case we need to correct the insertion position if it is greater than the initial position.
We can take advantage of the boolean/integer equivalence by subtracting to_index>from_index (1 if True, 0 otherwise)
def move(lst, from_index, to_index):
lst.insert(to_index-(to_index>from_index), lst.pop(from_index))
l = [1, 2, 3, 4, 5, 6]
move(l, 1, 3)
print(l)
# [1, 3, 2, 4, 5, 6]
This would work,
def move(lst, from_index, to_index):
new_lst = lst[:to_index] + [lst[from_index]] + [item for item in lst[to_index:]]
new_lst.pop(from_index + 1)
return new_lst
lst = [1, 2, 3, 4, 5, 6]
move(lst, 3, 1)
Output -
[1, 4, 2, 3, 5, 6]
The main problem you're seeing right now relates to how your insrt function works, as compared to how you're calling it.
The way you've written it, insert returns a new list containing the values from the old list plus one new value at a particular index. But when you call it in move, you seem to be expecting it to modify the given list in place. It doesn't do that. You need to change at least one of the two functions so that they match up. Either make insrt work in-place, or make move save the value returned by it rather than throwing it away.
Anyway, here's a very simple tweak to your move function that makes it work properly with the insrt function as you've shown it:
def move(lst, from_index, to_index):
a = lst[from_index]
del lst[from_index]
new_lst = insrt(lst, a, to_index) # save the new list after the insert
return new_lst # and then return it
I'd note that using del on the earlier line already modifies the lst passed in, so this design may not make a whole lot of sense. You might want to modify insrt to work in place instead, to be consistent. That would look something like this (which would work with your original move):
def insrt(lst, what, where):
length = my_len(lst)
lst.append(None) # add a dummy value at the end of the list
for i in range(length-1, where, -1): # iterate backwards from the end
lst[i] = lst[i - 1] # shift each value back
lst[where] = what # plug in the new value
# no return any more, this version works in place

function that concatenates two lists python Codecademy

I am stucked at ex 18,chapter lists and function from CODECADEMY.
"Create a function called flatten that takes a single list and concatenates all the sublists that are part of it into a single list."
"1 On line 3, define a function called flatten with one argument called lists.
2 Make a new, empty list called results.
3 Iterate through lists. Call the looping variable numbers.
4 Iterate through numbers.
5 For each number, .append() it to results.
6 Finally, return results from your function."
Here is my code:
And the error:
Oops, try again. flatten([[1, 2], [3, 4]]) returned [1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4] instead of [1, 2, 3, 4]
n = [[1, 2, 3], [4, 5, 6, 7, 8, 9]]
# Add your function here
results=[]
def flatten(lists):
for numbers in lists:
for number in numbers:
results.append(number)
return results
print flatten(n)
I can't figure out what is wrong.
Your mistake is that you declared the results list globally, while you should have declared it in the scope of your function.
Because results is outside of the function, it doesn't reset when you run flatten() the second time. Instead, the newly flattened array is added on top of the one from the previous run.
You should move results inside your function like so:
def flatten(lists):
results[]
for numbers in lists:
for number in numbers:
results.append(number)
return results
That way it resets every time you run the function and thus returns the correct result.
Convert this:
results=[]
def flatten(lists):
to this:
def flatten(lists):
results=[]
Is you data input wrong?
If you replace you first line of code
n = [[1, 2, 3], [4, 5, 6, 7, 8, 9]]
with
n = [[1, 2], [3, 4]].
The output will be
[1, 2, 3, 4]
Also, you should place results=[] inside function def flatten(lists):
def flatten(lists):
results=[]
for numbers in lists:
for number in numbers:
results.append(number)
return results

Why does my loop rotate only once?

I'm supposed to write a function which gets a list and rotates it several times (number of rotation is given)
def rotate1(lst):
print(lst[-1:]+lst[:-1])
def rotatek_v1(lst,k):
for i in range(0,k):
rotate1(lst)
print(lst)
for some reason, in the second function it rotates only once but I need it to rotate k times (I need it to run the function rotate1 k times), what should I fix?
Thanks
You need to assign the value back
def rotate1(lst):
return lst[-1:]+lst[:-1] # here
def rotatek_v1(lst,k):
for i in range(0,k):
lst = rotate1(lst) # and here
print(lst)
You are not modifying any value, you are just printing, so each time you iterate, you will get the same value. You have to use the return statement, so you can use the result and assign it to a variable, like:
def some_function():
return 100
some_var = some_function()
Applying it to your case:
def rotate1(lst):
return (lst[-1:] + lst[:-1])
def rotatek_v1(lst, k):
for i in range(0, k):
lst = rotate1(lst)
print lst
Demo:
>>> rotatek_v1([1, 2, 3, 4, 5], 6)
>>> [5, 1, 2, 3, 4]
[4, 5, 1, 2, 3]
[3, 4, 5, 1, 2]
[2, 3, 4, 5, 1]
[1, 2, 3, 4, 5]
[5, 1, 2, 3, 4]
Correct code should be this -
def rotate1(lst):
print(lst[-1:]+lst[:-1]);
def rotatek_v1(lst,k):
for i in range(0,k): rotate1(lst);
print(lst);
You have to align print statement in first function in right way like this
def rotate1(lst):
print(lst[-1:]+lst[:-1])
and in case of return then
def rotate1(lst):
return (lst[-1:]+lst[:-1])

List and Integer Python

I am trying to write a code that changes the position of an integer inside a list (basically swaps the position with another integer)
I have tried to use all logic, but still can't understand why my code is messing up:
SpecialNum = 10
def number_move(move_number):
for elements in range(len(move_number)):
if ( SpecialNum != move_number[-1]):
x = move_number.index(SpecialNum)
y = move_number.index(SpecialNum)+1
move_number[y], move_number[x] = move_number[x], move_number[y]
return (move_number)
the output should be:
[1,2,3,10,4,5,6]
>>>[1,2,3,4,10,5,6]
but output comes as:
[1,2,3,4,5,6,10]
Assuming your actual indentation looks like this:
SpecialNum = 10
def number_move(move_number):
for elements in range(len(move_number)):
if ( SpecialNum != move_number[-1]):
x = move_number.index(SpecialNum)
y = move_number.index(SpecialNum)+1
move_number[y], move_number[x] = move_number[x], move_number[y]
return move_number
… the problem is that you're swapping the 10 to the right over and over in a loop, until it reaches the very end.
If that isn't what you want, why do you have the for elements in range(len(move_number)) in the first place? Just take it out, and it will only get swapped right once.
As a side note, you rarely need range(len(eggs)); you can just do for egg in eggs (or, if you need the index along with the actual object, for index, egg in enumerate(eggs)).
Also, you've got a whole lot of extra parentheses that aren't needed, and make the code harder to read.
Meanwhile, every call to index has to search the entire list to find your object's position; if you already know the position, it's better to just use it. Not only is it a lot faster, and simpler, it's also more robust—if there are two elements of the list with the same value, index can only find the first one. In your case, there's no obvious way around using index, but at least you can avoid calling it twice.
Putting that together:
SpecialNum = 10
def number_move(move_number):
x = move_number.index(SpecialNum)
y = x + 1
if y != len(move_number):
move_number[y], move_number[x] = move_number[x], move_number[y]
Finally, I said there's no obvious way around using index… but is there a non-obvious way? Sure. If you're going to call index repeatedly on the same object, we can make the last-found index part of the interface to the function, or we can even store a cache inside the function. The simplest way to do this is to turn the whole thing into a generator. A generator that mutates its arguments can be kind of confusing, so let's make it return copies instead. And finally, to make it customizable, let's take a parameter so you can specify a different SpecialNum than 10.
SpecialNum = 10
def number_move(move_number, special_num=SpecialNum):
for x, element in reversed(list(enumerate(move_number))):
if element == special_num:
while x+1 < len(move_number):
move_number = (move_number[:x] +
[move_number[x+1], move_number[x]] +
move_number[x+2:])
yield move_number
x += 1
Now, it'll move all of the 10s to the end, one step at a time. Like this:
>>> n = [1, 10, 2, 3, 10, 4, 5, 6]
>>> for x in number_move(n):
... print(x)
[1, 10, 2, 3, 4, 10, 5, 6]
[1, 10, 2, 3, 4, 5, 10, 6]
[1, 10, 2, 3, 4, 5, 6, 10]
[1, 2, 10, 3, 4, 5, 6, 10]
[1, 2, 3, 10, 4, 5, 6, 10]
[1, 2, 3, 4, 10, 5, 6, 10]
[1, 2, 3, 4, 5, 10, 6, 10]
[1, 2, 3, 4, 5, 6, 10, 10]
[1, 2, 3, 4, 5, 6, 10, 10]
You don't need the for loop :)
def number_move(move_number):
x = move_number.index(SpecialNum)
y = move_number.index(SpecialNum)+1
move_number[y], move_number[x] = move_number[x], move_number[y]
Alternative:
>>> def number_move(m, i):
num = m.pop(i)
m.insert(i+1, num)
return m
>>> l = number_move([1,2,3,10,4,5,6], 3)
>>> l
[1, 2, 3, 4, 10, 5, 6]

Categories

Resources