My
createIndividual
courses
PEOPLE
def createIndividual(courses):
# Courses is equal to an individual, but
# without people
individual = courses.copy()
for course in individual:
myPeople = PEOPLE.copy()
random.shuffle(myPeople)
for table in course:
while len(table) < table.maximum:
table.append(myPeople.pop())
return individual
course
courses
[[[], [], []],
[[], [], []]]
Table()
maximum
maximum
Table
PEOPLE
[1, 2, 3, 4, 5, 6, 7, 8, 9]
individual
[[[4, 3, 8], [5, 9, 2], [1, 6, 7]],
[[4, 3, 8], [5, 9, 2], [1, 6, 7]]]
individual
[[[9, 8, 3], [7, 2, 1], [6, 5, 4]],
[[9, 8, 3], [7, 2, 1], [6, 5, 4]]]
course
individual
course
individual
This question really needs more detail to answer it with confidence, but since there's a common error that could produce this weird result (and since I wasn't able to reproduce it any other way)...
I think the problem is not in your createIndividual
function, but in the data structure you're feeding it. Here's a bit of my main
function that produced exactly the random output you expected:
from pprint import pprint
# pprint is essential for pretty-printing deeply nested data.
class Table(object):
...
# Guesswork on my part, plus a custom __str__ and __repr__.
def main():
# This creates a list of two lists-of-three-Tables.
distinct_courses = [[Table() for __ in range(3)] for __ in range(2)]
filled_courses = createIndividual(distinct_courses)
pprint(filled_courses)
Output:
[[Table([1, 2, 3]), Table([5, 8, 6]), Table([7, 4, 9])],
[Table([7, 5, 3]), Table([2, 6, 8]), Table([9, 1, 4])]]
To reproduce your problem, I had to create courses
using the list-multiplication syntax, which doesn't do what most beginners (and some tutorials) think it does:
4.6.1. Common Sequence Operations
[table omitted]
Notes:
- [...] Note that items in the sequence s are not copied; they are referenced multiple times. This often haunts new Python programmers; [...]
Further explanation is available in the FAQ entry How do I create a multidimensional list?.
I'm guessing somewhere in your code, you did something like this:
def bogus_main():
# This creates a single lists-of-three-Tables...
course = [Table() for __ in range(3)]
# ...then creates a list of two references to the _same_ list.
aliased_courses = [course] * 2
filled_courses = createIndividual(aliased_courses)
pprint(filled_courses)
Output using the aliased lists:
[[Table([7, 9, 2]), Table([8, 6, 1]), Table([5, 3, 4])],
[Table([7, 9, 2]), Table([8, 6, 1]), Table([5, 3, 4])]]
Since both courses[0]
and courses[1]
point to the same list, two weird things happen. First, the contents of the "two" course
s will be the same, as you've already noticed. Each update seems to add a pair of identical Table
s to two different lists, although it's really just adding one Table
to one list... and then printing that list twice... You can see this in action by adding an extra pprint
to createIndividual
:
[[Table([]), Table([]), Table([])],
[Table([]), Table([]), Table([])]]
[[Table([7, 9, 2]), Table([]), Table([])],
[Table([7, 9, 2]), Table([]), Table([])]]
[[Table([7, 9, 2]), Table([8, 6, 1]), Table([])],
[Table([7, 9, 2]), Table([8, 6, 1]), Table([])]]
[[Table([7, 9, 2]), Table([8, 6, 1]), Table([5, 3, 4])],
[Table([7, 9, 2]), Table([8, 6, 1]), Table([5, 3, 4])]]
[[Table([7, 9, 2]), Table([8, 6, 1]), Table([5, 3, 4])],
[Table([7, 9, 2]), Table([8, 6, 1]), Table([5, 3, 4])]]
[[Table([7, 9, 2]), Table([8, 6, 1]), Table([5, 3, 4])],
[Table([7, 9, 2]), Table([8, 6, 1]), Table([5, 3, 4])]]
[[Table([7, 9, 2]), Table([8, 6, 1]), Table([5, 3, 4])],
[Table([7, 9, 2]), Table([8, 6, 1]), Table([5, 3, 4])]]
Second, notice how the last three "updates" don't actually change anything? Something should have failed when you tried to add 18 values to only 9 slots. Unfortunately, the maximum
field that protects you from over-filling a Table
also saves you from the error that could have tipped you off earlier. From createIndividual
:
for table in course:
while len(table) < table.maximum:
# Once the "first" aliased course list is full, this will
# never pop another person, because there's no place to
# store them.
table.append(myPeople.pop())
print(len(myPeople)) # My addition.
# Prints 6, 3, and 0 during the first `course`, then prints
# 9, 9, and 9... myPeople never changes the second time through.
After the "first" (really, the only) three Table
s are filled, they're all at maximum
length, so neither individual
or myPeople
will change again.
Contrast the list-comprehension version in my main
, which creates six different Table
objects, like you'd expect:
[[Table([]), Table([]), Table([])],
[Table([]), Table([]), Table([])]]
[[Table([1, 2, 3]), Table([]), Table([])],
[Table([]), Table([]), Table([])]]
[[Table([1, 2, 3]), Table([5, 8, 6]), Table([])],
[Table([]), Table([]), Table([])]]
[[Table([1, 2, 3]), Table([5, 8, 6]), Table([7, 4, 9])],
[Table([]), Table([]), Table([])]]
[[Table([1, 2, 3]), Table([5, 8, 6]), Table([7, 4, 9])],
[Table([7, 5, 3]), Table([]), Table([])]]
[[Table([1, 2, 3]), Table([5, 8, 6]), Table([7, 4, 9])],
[Table([7, 5, 3]), Table([2, 6, 8]), Table([])]]
[[Table([1, 2, 3]), Table([5, 8, 6]), Table([7, 4, 9])],
[Table([7, 5, 3]), Table([2, 6, 8]), Table([9, 1, 4])]]