builtin_function_or_method' object has no attribute shuffle - python

Hi I am trying to define a function that returns a shuffled list l1 without changing the original list l using the method random.shuffle but I got this error message:
builtin_function_or_method object has no attribute shuffle
import random
def shuffle_list(l):
l1=random.shuffle(l)
return(l1)

from random import shuffle
def shuffle_list(l):
def shuffle_list(l):
shuffle(l)
return l
Import shuffle directly and return l and don't save it into any variable because l1 = shuffle(l) will return None
I know this is coming late.

Replace all instances of random.shuffle() with shuffle() after changing the import random with from random import shuffle.

random.shuffle() modifies the list in-place and doesn't return anything, so you need to first make a copy of the list argument, shuffle, and then return it, in order to preserve the original:
import random
def shuffle_list(lst):
lst2 = lst.copy()
random.shuffle(lst2)
return lst2
items = list(range(10))
shuffled = shuffle_list(items)
print(items) # => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print(shuffled) # => [6, 2, 1, 9, 3, 0, 8, 4, 5, 7] (or some other rearrangement)

random.shuffle changes the order of the list in place and returns None.
>>> lst = [1,2,3]
>>> shuffle_list(lst)
>>> print lst
[3, 1, 2]
>>> shuffle_list(lst)
>>> print lst
[1, 3, 2]
So if you don't care about the order of the original list, your code could simply be:
import random
random.shuffle(l)

Related

Removing duplicates from a list but returning the same list

I need to remove the duplicates from a list but return the same list.
So options like:
return list(set(list))
will not work for me, as it creates a new list instead.
def remove_extras(lst):
for i in lst:
if lst.count(i)>1:
lst.remove(i)
return lst
Here is my code, it works for some cases, but I dont get why it does not work for remove_extras([1,1,1,1]), as it returns [1,1] when the count for 1 should be >1.
You can use slice assignment to replace the contents of the list after you have created a new list. In case order of the result doesn't matter you can use set:
def remove_duplicates(l):
l[:] = set(l)
l = [1, 2, 1, 3, 2, 1]
remove_duplicates(l)
print(l)
Output:
[1, 2, 3]
You can achieve this using OrderedDict which removes the duplicates while maintaining order of the list.
>>> from collections import OrderedDict
>>> itemList = [1, 2, 0, 1, 3, 2]
>>> itemList[:]=OrderedDict.fromkeys(itemList)
>>> itemList
[1, 2, 0, 3]
This has a Runtime : O(N)

How can I select entries from list in random order in python

Question is simple. I have a list of say 10 entries, I am running a loop over it. What i want here is getting each entry exactly once but in random order.
What is the best and most pythonic way to do it?
You can use random.sample, it returns random elements preventing duplicates:
>>> import random
>>> data = range(10)
>>> print(random.sample(data, len(data)))
[2, 4, 8, 7, 0, 5, 6, 3, 1, 9]
The original list remains unchanged.
You can use random.shuffle:
In [1]: import random
In [3]: a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [4]: random.shuffle(a)
In [5]: a
Out[5]: [3, 6, 9, 1, 8, 0, 4, 7, 5, 2]
Shuffle the list first by importing the random module and using the shuffle function:
import random
x = ... # A list
random.shuffle(x)
Then loop over your list. Note that shuffle mutates the original list and does not return the shuffled version.
You could use shuffle:
import random
random.shuffle(yourlist)
# loop as you would normally
for item in yourlist:
.....
you could use random.shuffle():
import random
original = range(10)
# Make a copy of the original list, as shuffling will be in-place
shuffled = list(original)
random.shuffle(shuffled)
Valid remark from Jean-François Fabre: if you were to use the original variable and pass it directly to random.shuffle, Python would return an error, stating 'range' object does not support item assignment, as range returns a generator.
To solve this, simply replace the assignment with list(range(10)).
import random
s=set(range(10))
while len(s)>0:
s.remove(random.choice(yourlist(s)))
print(s)

Why does `mylist[:] = reversed(mylist)` work?

The following reverses a list "in-place" and works in Python 2 and 3:
>>> mylist = [1, 2, 3, 4, 5]
>>> mylist[:] = reversed(mylist)
>>> mylist
[5, 4, 3, 2, 1]
Why/how? Since reversed gives me an iterator and doesn't copy the list beforehand, and since [:]= replaces "in-place", I am surprised. And the following, also using reversed, breaks as expected:
>>> mylist = [1, 2, 3, 4, 5]
>>> for i, item in enumerate(reversed(mylist)):
mylist[i] = item
>>> mylist
[5, 4, 3, 4, 5]
Why doesn't the [:] = fail like that?
And yes, I do know mylist.reverse().
CPython list slice assigment will convert the iterable to a list first by calling PySequence_Fast. Source: https://hg.python.org/cpython/file/7556df35b913/Objects/listobject.c#l611
v_as_SF = PySequence_Fast(v, "can only assign an iterable");
Even PyPy does something similar:
def setslice__List_ANY_ANY_ANY(space, w_list, w_start, w_stop, w_iterable):
length = w_list.length()
start, stop = normalize_simple_slice(space, length, w_start, w_stop)
sequence_w = space.listview(w_iterable)
w_other = W_ListObject(space, sequence_w)
w_list.setslice(start, 1, stop-start, w_other)
Here space.listview will call ObjSpace.unpackiterable to unpack the iterable which in turn returns a list.

Generate random numbers in range without repetition

I'm creating a trivia game and need to call 10 questions (functions) in a random order. I could simply generate a random integer 1-10 and use if statements to call each function, but I need to make sure that no questions are called more than once, so I need to generate random numbers without repetition.
def Trivia_Snap():
question_1()
question_2()
question_3()
question_4()
question_5()
question_6()
question_7()
question_8()
question_9()
question_10()
You can put the calls to the functions in a list
l = [question_1, question_2...]
Then select from that list randomly without replacment
import random
rand_l = random.sample(l, len(l))
for f in rand_l:
f()
EDIT: per comments below you can also use shuffle
random.shuffle(l)
for f in l:
f()
>>> import random
create a list of numbers
>>> l = range(0,11)
>>> l
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
...and shuffle them in place with random.shuffle()
>>> random.shuffle(l)
>>> l
[5, 7, 4, 2, 1, 8, 6, 10, 9, 0, 3]
>>> random.shuffle(l)
>>> l
[1, 5, 9, 4, 10, 6, 2, 7, 0, 3, 8]
import random
qlist = [i for i in range(0,10)]
random.shuffle(qlist)
for q in qlist:
print q
Suppose you have a list of questions:
questions = ['Q: foo?', 'Q: bar?', Q: 'baz?']
With the following function you can present all these questions for an user to answer. Everytime you call the function, the questions will be presented in a different order. It will return a list of tuples in which each tuple represents the number of the question answered, as enumerated from the original question list, and the answer to the question:
from random import shuffle
def trivia_snap(questions):
q = list(enumerate(questions))
shuffle(q)
answers = []
for question in q:
answers.append((question[0], input(question[1] + " Answer: ")))
return sorted(answers)
>>> trivia_snap(questions)
>>> Q: foo? Answer: "Homer"
>>> Q: baz? Answer: "Marge"
>>> Q: bar? Answer: "Lisa"
>>> [(0, "Homer"), (1, "Lisa"), (2, "Marge"), ]
Hope it helps!

Shuffle in Python

Is there a straightforward way to RETURN a shuffled array in Python rather than shuffling it in place?
e.g., instead of
x = [array]
random.shuffle(x)
I'm looking for something like
y = shuffle(x)
which maintains x.
Note, I am not looking for a function, not something like:
x=[array]
y=x
random.shuffle(x)
sorted with a key function that returns a random value:
import random
sorted(l, key=lambda *args: random.random())
Or
import os
sorted(l, key=os.urandom)
It would be pretty simple to implement your own using random. I would write it as follows:
def shuffle(l):
l2 = l[:] #copy l into l2
random.shuffle(l2) #shuffle l2
return l2 #return shuffled l2
Just write your own.
import random
def shuffle(x):
x = list(x)
random.shuffle(x)
return x
x = range(10)
y = shuffle(x)
print x # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
print y # [2, 5, 0, 4, 9, 3, 6, 1, 7, 8]
There is no function you are looking for. Just copy a list.
You could use numpy.random.permutation for either a list or array, but is the right function if you have a numpy array already. For lists with mixed types, converting to a numpy array will do type conversions.
import numpy as np
my_list = ['foo', 'bar', 'baz', 42]
print list(np.random.permutation(my_list))
# ['bar', 'baz', '42', 'foo']
Using this as a demo elsewhere so thought it may be worth sharing:
import random
x = shuffleThis(x)
def shuffleThis(y):
random.shuffle(y)
return(y)
#end of Shuffle Function
Hope this is useful.

Categories