0

Observe the following code:

class permcom:
  def __init__(self, INPUT_SET, IS_PERM, REPETITION):
    self.end_set = []
    self.input_set = INPUT_SET
    self.is_perm = IS_PERM
    self.repetition = REPETITION
  def helpfunc(self, seen, depth, current):
    if depth == 0:
      self.end_set.append(seen)
    else:
      for i in range(0, len(self.input_set)):
        if(self.repetition):
          seen.append(self.input_set[i])
          if(self.is_perm):
            self.helpfunc(seen, depth - 1, 0)
          else:
            self.helpfunc(seen, depth - 1, i)
          del seen[-1]

# return all permutations with repetition
def rapwr(INPUT_SET, subset_size):
  instance = permcom(INPUT_SET, True, True)
  A = []
  instance.helpfunc(A, subset_size, 0)
  return instance.end_set

A = [1,2,3]
B = rapwr(A, 2)
for i in range(0, len(B)):
  print B[i]

The output is the following:

[]
[]
[]
[]
[]
[]
[]
[]
[]

However, the intended output is this:

[1, 1]
[1, 2]
[1, 3]
[2, 1]
[2, 2]
[2, 3]
[3, 1]
[3, 2]
[3, 3]

I've spent way too much time looking at this code and, unfortunately, I still cannot figure out exactly what's wrong. There must be something fundamental that I'm not understanding about how member variables work in Python, but I still don't quite understand what's going on here and why the code isn't working. Can somebody explain this?

1
  • Could you explain in exhaustive detail what your code is supposedly doing? Commented Jan 21, 2018 at 5:59

1 Answer 1

3

Short answer

What you need is list slicing [:]. Changing the statement

if depth == 0:
  self.end_set.append(seen)

to

if depth == 0:
  self.end_set.append(seen[:])

Gives the expected answer

Long answer

Try this sample code in a python interpreter

a = [1,2]
b = []
b.append(a)
a[0] = 3
print b
# output is [[3, 2]]

Now try this code

a = [1,2]
b = []
b.append(a[:])
a[0] = 3
print b
# output is [[1, 2]]

Why did this happen? In the first case when you appended a to the list b, it was not the value of a that was appended, it was a reference/tag to the [1,2] value. You can verify this by printing id(b[0]) and id(a). Both will be the same value. Hence when you modify any value in the a list, the value in the b list also changes.

Same is the case in your code. Since you are doing del seen[-1], the corresponding value in self.end_set is also removed. You can confirm this by printing the value of self.end_set in the depth == 0 block.

To avoid this you append a clone of one list to the other list. This is done by using the splicing syntax [:]. This creates a copy of the list from the start to the end of the list. You can learn more about slicing here.

PS: Try printing the id() of the two lists when you use slicing, the values will be different

Here is what I got

a = [1,2]
b = []
b.append(a)
print id(b[0])
#output is 43337352L
print id(a)
#output is 43337352L
b = []
b.append(a[:])
print id(b[0])
#output is 43337608L

Take a look at this python memory model diagram for a better understanding of the above

Update: some advice

  1. Since B and self.input_set are both lists, prefer using the idiomatic for i in B and for i in self.input_set.
  2. Make sure your function names are understandable. It might help you out someday. Generally if you are made to write a comment for a variable or function name, it is better to name the function/variable with a shortened version of the comment itself. So rapwr can be renamed to return_all_permutations_with repetition. Though the name is large, its easy to understand what it does without looking at the method body now.
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.