简体   繁体   中英

Recursive Class Objects and Member Variables in Python

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?

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM