Class Python 中的递归对象和成员变量
Recursive Class Objects and Member Variables in Python
遵守以下代码:
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]
输出如下:
[]
[]
[]
[]
[]
[]
[]
[]
[]
然而,预期的输出是这样的:
[1, 1]
[1, 2]
[1, 3]
[2, 1]
[2, 2]
[2, 3]
[3, 1]
[3, 2]
[3, 3]
我花了太多时间查看这段代码,不幸的是,我仍然无法弄清楚到底出了什么问题。关于成员变量在 Python 中的工作原理,肯定有一些我不了解的基本知识,但我仍然不太了解这里发生了什么以及为什么代码不起作用。有人可以解释一下吗?
简答
你需要的是列表切片[:]
。更改语句
if depth == 0:
self.end_set.append(seen)
到
if depth == 0:
self.end_set.append(seen[:])
给出了预期的答案
长答案
在 python 解释器中尝试此示例代码
a = [1,2]
b = []
b.append(a)
a[0] = 3
print b
# output is [[3, 2]]
现在试试这个代码
a = [1,2]
b = []
b.append(a[:])
a[0] = 3
print b
# output is [[1, 2]]
为什么会这样?在第一种情况下,当您将 a
附加到列表 b
时,附加的不是 a
的 value,而是 reference/tag 到 [1,2]
值。您可以通过打印 id(b[0])
和 id(a)
来验证这一点。 两者将是相同的值。因此,当您修改 a
列表中的任何值时,b
列表中的值也会更改。
您的代码也是如此。由于您正在执行 del seen[-1]
,因此 self.end_set
中的相应值也被删除。您可以通过在 depth == 0
块中打印 self.end_set
的值来确认这一点。
为避免这种情况,您将一个列表的克隆附加到另一个列表。这是通过使用拼接语法 [:]
完成的。这会从列表的开头到结尾创建列表的副本。您可以了解有关切片的更多信息 here.
PS: 使用切片时尝试打印两个列表的id()
,值会不同
这是我得到的
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
看看这个 python memory model diagram 可以更好地理解上面的内容
更新:一些建议
- 由于
B
和 self.input_set
都是列表,因此更喜欢使用惯用的 for i in B
和 for i in self.input_set
。
- 确保您的函数名称易于理解。它可能有一天会帮助你。一般来说,如果你被要求为变量或函数名写注释,最好用注释本身的缩写形式命名 function/variable。所以
rapwr
可以重命名为return_all_permutations_with repetition
。虽然名字很大,但是现在不看方法体也很容易理解它的作用。
遵守以下代码:
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]
输出如下:
[]
[]
[]
[]
[]
[]
[]
[]
[]
然而,预期的输出是这样的:
[1, 1]
[1, 2]
[1, 3]
[2, 1]
[2, 2]
[2, 3]
[3, 1]
[3, 2]
[3, 3]
我花了太多时间查看这段代码,不幸的是,我仍然无法弄清楚到底出了什么问题。关于成员变量在 Python 中的工作原理,肯定有一些我不了解的基本知识,但我仍然不太了解这里发生了什么以及为什么代码不起作用。有人可以解释一下吗?
简答
你需要的是列表切片[:]
。更改语句
if depth == 0:
self.end_set.append(seen)
到
if depth == 0:
self.end_set.append(seen[:])
给出了预期的答案
长答案
在 python 解释器中尝试此示例代码
a = [1,2]
b = []
b.append(a)
a[0] = 3
print b
# output is [[3, 2]]
现在试试这个代码
a = [1,2]
b = []
b.append(a[:])
a[0] = 3
print b
# output is [[1, 2]]
为什么会这样?在第一种情况下,当您将 a
附加到列表 b
时,附加的不是 a
的 value,而是 reference/tag 到 [1,2]
值。您可以通过打印 id(b[0])
和 id(a)
来验证这一点。 两者将是相同的值。因此,当您修改 a
列表中的任何值时,b
列表中的值也会更改。
您的代码也是如此。由于您正在执行 del seen[-1]
,因此 self.end_set
中的相应值也被删除。您可以通过在 depth == 0
块中打印 self.end_set
的值来确认这一点。
为避免这种情况,您将一个列表的克隆附加到另一个列表。这是通过使用拼接语法 [:]
完成的。这会从列表的开头到结尾创建列表的副本。您可以了解有关切片的更多信息 here.
PS: 使用切片时尝试打印两个列表的id()
,值会不同
这是我得到的
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
看看这个 python memory model diagram 可以更好地理解上面的内容
更新:一些建议
- 由于
B
和self.input_set
都是列表,因此更喜欢使用惯用的for i in B
和for i in self.input_set
。 - 确保您的函数名称易于理解。它可能有一天会帮助你。一般来说,如果你被要求为变量或函数名写注释,最好用注释本身的缩写形式命名 function/variable。所以
rapwr
可以重命名为return_all_permutations_with repetition
。虽然名字很大,但是现在不看方法体也很容易理解它的作用。