在python中将类对象附加到列表中的陷阱

在python中将类对象附加到列表中的陷阱,python,list,Python,List,在将对象附加到python列表时,我不太清楚。 由于原始代码太复杂,太长,无法在这里发布,因此我设计了一个简单的示例来展示其效果。 为了解释这一点,我设法重新编写了代码,使其正常运行并提供正确的结果。但这需要我很多时间来调试。 发生的原因我还不清楚。所以我很高兴你能解释我为什么会这样 好的,我们走吧 这里是代码的工作版本。最后,您可以看到,它提供了一个输出并按预期工作: import numpy as np class foo(): def __init__(self):

在将对象附加到python列表时,我不太清楚。 由于原始代码太复杂,太长,无法在这里发布,因此我设计了一个简单的示例来展示其效果。 为了解释这一点,我设法重新编写了代码,使其正常运行并提供正确的结果。但这需要我很多时间来调试。 发生的原因我还不清楚。所以我很高兴你能解释我为什么会这样

好的,我们走吧

这里是代码的工作版本。最后,您可以看到,它提供了一个输出并按预期工作:

import numpy as np

class foo():
    def __init__(self):
        self.x = np.random.random()
    
    def move(self, value):
        self.x += value
        
p  = []
p2 = []

for _ in range(10):
    
    z = foo()
    z.move(4)
    p2.append(z)
    
p = p2
    
for i in range(10):
    print("Print x: {}".format(p[i].x))
运行代码时,我会得到以下输出(这与时间不同,因为我调用了随机生成器函数)。但它起作用了!:

Print x: 4.18111236900313
Print x: 4.399997493230335
Print x: 4.594324823463079
Print x: 4.8205743285019045
Print x: 4.125578603746895
Print x: 4.430324972670274
Print x: 4.135255992051397
Print x: 4.9479568256336295
Print x: 4.789025666744436
Print x: 4.261426793909385
现在我向大家展示了代码的第一个版本,这让我非常头痛,也非常愤怒。请注意,我稍微更改了第二个for循环,因为我发现它更符合逻辑。我直接调用方法
move()
,并在一行而不是两行中调用方法
append()
。 以下是版本:

import numpy as np

class foo():
    def __init__(self):
        self.x = np.random.random()
    
    def move(self, value):
        self.x += value
        
p  = []
p2 = []

for _ in range(10):
    
    z = foo()
    p2.append(z.move(4))    # Here is the only change to the code!
    
p = p2

    
for i in range(10):
    print("Print x: {}".format(p[i].x))
但运行此代码时,我会出现以下错误:

AttributeError: 'NoneType' object has no attribute 'x'
然后我发现列表p和p2没有元素。 为什么? 我很高兴理解为什么

编辑

正如下面所建议的,我意识到
move()
方法不会返回任何内容

因此,我将代码更改如下:

import numpy as np

class foo():
    def __init__(self):
        self.x = np.random.random()
    
    def move(self, value):
        
        output    = foo()
        output.x += value
        
        return output      # <= Here the change
        
p  = []
p2 = []

for _ in range(10):
    
    z = foo()
    p2.append(z.move(4))
    
p = p2

    
for i in range(10):
    print("Print x: {}".format(p[i].x))
将numpy导入为np
类foo():
定义初始化(自):
self.x=np.random.random()
def移动(自身、值):
输出=foo()
输出.x+=值
返回输出#您的
z.move()
方法返回
None

如果要更改此行为,请将其更改为返回
self

    def move(self, value):
        self.x += value
        return self

首先制作对象的副本,有助于保持控制(不可变的简单数据对象比可变对象更有利于可维护性)

如果您真的想保持事物的易变性,您可以在循环中而不是p2中执行上述操作。append(z.move(4)):


我认为您可能真正想问的是,为什么append存储方法返回的内容而不是对象。这是因为当一个方法被用于赋值时,它会立即被执行,并且它的返回值会在它的位置被使用

这就是为什么可以将方法传递给只设计为接受其返回值的函数

例如:

def square(x):
    return x*x

def print_variable(variable_to_print):
    print(variable_to_print)

print_variable(square(2))
在本例中,print_变量不接收平方(2),而是接收值4

这就是为什么move需要返回self,以便按照您使用它的方式更新x

实现这一点的一种更简单且争议较少的方法可能是在实例化时简单地执行此方法

class foo(x=None):
    def __init__(self):
        self.x = np.random.random()
        if x:
            self.x += x

    def move(self, value):
        self.x += value

p  = []
p2 = []

for _ in range(10):
    p2.append(foo(4))

p = p2

foo.move
不返回
foo
;它返回
None
(隐式返回,因为没有
return
语句。这“修复”了问题,但违反了一个相当合理的约定,即方法应该改变其对象或返回值,但不能同时返回这两个值。“约定”请引用?每次调用
move\u immutable
时都必须导入
import copy
?所有导入都需要在文件的开头,但为了清晰起见,它在那里。我会编辑一点。
def square(x):
    return x*x

def print_variable(variable_to_print):
    print(variable_to_print)

print_variable(square(2))
class foo(x=None):
    def __init__(self):
        self.x = np.random.random()
        if x:
            self.x += x

    def move(self, value):
        self.x += value

p  = []
p2 = []

for _ in range(10):
    p2.append(foo(4))

p = p2