Python 引用和循环
Python references and loops
我习惯了 perl,其中引用和变量是明确分开的。开始使用 python 让生活变得非常混乱。我想循环工作,创建对象并将它们添加到列表中,但结果令人沮丧。我读过函数定义变量范围,但即使将我的循环代码封装在函数中似乎也无济于事。
举例如下:
mylist = []
class thing:
def __init__(self, stuff=[]):
self.stuff = stuff
for i in range(10):
obj = thing()
obj.stuff.append(1)
mylist.append(obj)
len(mylist[0].stuff)
我希望每个对象的“stuff”列表的长度为 1,但结果为 10。我知道我可以使用深拷贝来明确地将新创建的对象与之前创建的对象分开,但是有没有更漂亮的方法?
这里的问题是您使用相同的 list
对象作为您使用 thing()
创建的所有对象的默认值
class thing:
def __init__(self, stuff=[]):
self.stuff = stuff
for i in range(3):
obj = thing()
print(id(obj.stuff))
# Output
140710582002368
140710582002368
140710582002368
一个可能的解决方案是不使用默认值,它看起来像这样
class thing:
def __init__(self, stuff):
self.stuff = stuff
for i in range(10):
obj = thing(stuff = list())
print(id(obj.stuff))
# Output
139911079590016
139911079590144
139911079590208
这会为 stuff
成员分配一个新的 list
for 循环的每次迭代。
尝试一下,当可变列表对象被定义为参数时,它被传递给 classes。
class thing:
def __init__(self, stuff=[]):
self.stuff = stuff
for i in range(10):
obj = thing()
print(obj)
obj.stuff.append(1)
print(obj.stuff)
mylist.append(obj)
print(mylist)
<main.thing object at 0x10ff4b5f8>
[1]
[<main.thing object at 0x10ff4b5f8>]
<main.thing object at 0x10ff4b978>
[1, 1]
[<main.thing object at 0x10ff4b5f8>, <main.thing object at 0x10ff4b978>]
<main.thing object at 0x10ff4b4e0>
[1, 1, 1]
[<main.thing object at 0x10ff4b5f8>, <main.thing object at 0x10ff4b978>, <main[=191= .thing 对象在 0x10ff4b4e0>]
<main.thing object at 0x10ff4b4a8>
[1, 1, 1, 1]
[<main.thing object at 0x10ff4b5f8>, <main.thing object at 0x10ff4b978>, <main[=191= .thing object at 0x10ff4b4e0>, <main.thing object at 0x10ff4b4a8>]
<main.thing object at 0x10ff4b5c0>
[1, 1, 1, 1, 1]
[<main.thing object at 0x10ff4b5f8>, <main.thing object at 0x10ff4b978>, <main[=191= .thing object at 0x10ff4b4e0>, <main.thing object at 0x10ff4b4a8>, <main.thing object at 0x10ff4b5c0>]
<main.thing object at 0x10ff4b588>
[1, 1, 1, 1, 1, 1]
[<main.thing object at 0x10ff4b5f8>, <main.thing object at 0x10ff4b978>, <main[=191= .thing object at 0x10ff4b4e0>, <main.thing object at 0x10ff4b4a8>, <main.thing object at 0x10ff4b5c0>, <main.thing object at 0x10ff4b588>]
<main.thing object at 0x10ff4b6d8>
[1, 1, 1, 1, 1, 1, 1]
[<main.thing object at 0x10ff4b5f8>, <main.thing object at 0x10ff4b978>, <main[=191= .thing object at 0x10ff4b4e0>, <main.thing object at 0x10ff4b4a8>, <main.thing object at 0x10ff4b5c0>, <main.thing object at 0x10ff4b588>, <main.thing object at 0x10ff4b6d8>]
<main.thing object at 0x10ff4b710>
[1, 1, 1, 1, 1, 1, 1, 1]
[<main.thing object at 0x10ff4b5f8>, <main.thing object at 0x10ff4b978>, <main[=191= .thing object at 0x10ff4b4e0>, <main.thing object at 0x10ff4b4a8>, <main.thing object at 0x10ff4b5c0>, <main.thing object at 0x10ff4b588>, <main.thing object at 0x10ff4b6d8>, <main.thing object at 0x10ff4b710>]
<main.thing object at 0x10ff4b668>
[1, 1, 1, 1, 1, 1, 1, 1, 1]
[<main.thing object at 0x10ff4b5f8>, <main.thing object at 0x10ff4b978>, <main[=191= .thing object at 0x10ff4b4e0>, <main.thing object at 0x10ff4b4a8>, <main.thing object at 0x10ff4b5c0>, <main.thing object at 0x10ff4b588>, <main.thing object at 0x10ff4b6d8>, <main.thing object at 0x10ff4b710>, <main.thing object at 0x10ff4b668>]
<main.thing object at 0x10ff4b748>
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[<main.thing object at 0x10ff4b5f8>, <main.thing object at 0x10ff4b978>, <main[=191= .thing object at 0x10ff4b4e0>, <main.thing object at 0x10ff4b4a8>, <main.thing object at 0x10ff4b5c0>, <main.thing object at 0x10ff4b588>, <main.thing object at 0x10ff4b6d8>, <main.thing object at 0x10ff4b710>, <main.thing object at 0x10ff4b668>, <main.thing object at 0x10ff4b748>]
在函数块中实例化列表时,它每次都会创建一个新列表。
下面是使用 class 时的输出。
class Thing:
__ init__(self, stuff):
if stuff:
self.stuff = stuff
else:
self.stuff = []
for i in range(10):
obj = thing()
print(obj)
obj.stuff.append(1)
print(obj.stuff)
mylist.append(obj)
print(mylist)
输出
<main.thing object at 0x10ff4b518>
[1]
[<main.thing object at 0x10ff4b518>]
<main.thing object at 0x10ff4b4e0>
[1]
[<main.thing object at 0x10ff4b518>, <main.thing object at 0x10ff4b4e0>]
<main.thing object at 0x10ff4b6d8>
[1]
[<main.thing object at 0x10ff4b518>, <main.thing object at 0x10ff4b4e0>, <main[=191= .thing 对象在 0x10ff4b6d8>]
<main.thing object at 0x10ff4b550>
[1]
[<main.thing object at 0x10ff4b518>, <main.thing object at 0x10ff4b4e0>, <main[=191= .thing object at 0x10ff4b6d8>, <main.thing object at 0x10ff4b550>]
<main.thing object at 0x10ff4b7b8>
[1]
[<main.thing object at 0x10ff4b518>, <main.thing object at 0x10ff4b4e0>, <main[=191= .thing object at 0x10ff4b6d8>, <main.thing object at 0x10ff4b550>, <main.thing object at 0x10ff4b7b8>]
<main.thing object at 0x10ff4b7f0>
[1]
[<main.thing object at 0x10ff4b518>, <main.thing object at 0x10ff4b4e0>, <main[=191= .thing object at 0x10ff4b6d8>, <main.thing object at 0x10ff4b550>, <main.thing object at 0x10ff4b7b8>, <main.thing object at 0x10ff4b7f0>]
<main.thing object at 0x10ff4b668>
[1]
[<main.thing object at 0x10ff4b518>, <main.thing object at 0x10ff4b4e0>, <main[=191= .thing object at 0x10ff4b6d8>, <main.thing object at 0x10ff4b550>, <main.thing object at 0x10ff4b7b8>, <main.thing object at 0x10ff4b7f0>, <main.thing object at 0x10ff4b668>]
<main.thing object at 0x10ff4b6a0>
[1]
[<main.thing object at 0x10ff4b518>, <main.thing object at 0x10ff4b4e0>, <main[=191= .thing object at 0x10ff4b6d8>, <main.thing object at 0x10ff4b550>, <main.thing object at 0x10ff4b7b8>, <main.thing object at 0x10ff4b7f0>, <main.thing object at 0x10ff4b668>, <main.thing object at 0x10ff4b6a0>]
<main.thing object at 0x10ff4b748>
[1]
[<main.thing object at 0x10ff4b518>, <main.thing object at 0x10ff4b4e0>, <main[=191= .thing object at 0x10ff4b6d8>, <main.thing object at 0x10ff4b550>, <main.thing object at 0x10ff4b7b8>, <main.thing object at 0x10ff4b7f0>, <main.thing object at 0x10ff4b668>, <main.thing object at 0x10ff4b6a0>, <main.thing object at 0x10ff4b748>]
<main.thing object at 0x10ff4b828>
[1]
[<main.thing object at 0x10ff4b518>, <main.thing object at 0x10ff4b4e0>, <main[=191= .thing object at 0x10ff4b6d8>, <main.thing object at 0x10ff4b550>, <main.thing object at 0x10ff4b7b8>, <main.thing object at 0x10ff4b7f0>, <main.thing object at 0x10ff4b668>, <main.thing object at 0x10ff4b6a0>, <main.thing object at 0x10ff4b748>, <main.thing object at 0x10ff4b828>]
这是 Python 的怪癖。参数的默认值仅在定义函数时构造一次。所以,你会得到这个奇怪的行为:
def f(x=[]):
x.append('x')
return x
f()
# ['x']
f()
# ['x', 'x']
f()
# ['x', 'x', 'x']
将 []
替换为 list()
无济于事,因为在定义函数时它仍然只会被计算一次。然后,每次调用该函数时,相同的对象将作为默认对象重用。
要解决这个问题,您需要使用哨兵,然后明确测试它是否已被覆盖。这是一个常见的模式:
def f(x=None):
if x is None:
x = []
x.append('x')
return x
f()
# ['x']
f()
# ['x']
f(['a', 'b'])
# ['a', 'b', 'x']
我习惯了 perl,其中引用和变量是明确分开的。开始使用 python 让生活变得非常混乱。我想循环工作,创建对象并将它们添加到列表中,但结果令人沮丧。我读过函数定义变量范围,但即使将我的循环代码封装在函数中似乎也无济于事。
举例如下:
mylist = []
class thing:
def __init__(self, stuff=[]):
self.stuff = stuff
for i in range(10):
obj = thing()
obj.stuff.append(1)
mylist.append(obj)
len(mylist[0].stuff)
我希望每个对象的“stuff”列表的长度为 1,但结果为 10。我知道我可以使用深拷贝来明确地将新创建的对象与之前创建的对象分开,但是有没有更漂亮的方法?
这里的问题是您使用相同的 list
对象作为您使用 thing()
class thing:
def __init__(self, stuff=[]):
self.stuff = stuff
for i in range(3):
obj = thing()
print(id(obj.stuff))
# Output
140710582002368
140710582002368
140710582002368
一个可能的解决方案是不使用默认值,它看起来像这样
class thing:
def __init__(self, stuff):
self.stuff = stuff
for i in range(10):
obj = thing(stuff = list())
print(id(obj.stuff))
# Output
139911079590016
139911079590144
139911079590208
这会为 stuff
成员分配一个新的 list
for 循环的每次迭代。
尝试一下,当可变列表对象被定义为参数时,它被传递给 classes。
class thing:
def __init__(self, stuff=[]):
self.stuff = stuff
for i in range(10):
obj = thing()
print(obj)
obj.stuff.append(1)
print(obj.stuff)
mylist.append(obj)
print(mylist)
<main.thing object at 0x10ff4b5f8> [1] [<main.thing object at 0x10ff4b5f8>]
<main.thing object at 0x10ff4b978> [1, 1] [<main.thing object at 0x10ff4b5f8>, <main.thing object at 0x10ff4b978>]
<main.thing object at 0x10ff4b4e0> [1, 1, 1] [<main.thing object at 0x10ff4b5f8>, <main.thing object at 0x10ff4b978>, <main[=191= .thing 对象在 0x10ff4b4e0>]
<main.thing object at 0x10ff4b4a8> [1, 1, 1, 1] [<main.thing object at 0x10ff4b5f8>, <main.thing object at 0x10ff4b978>, <main[=191= .thing object at 0x10ff4b4e0>, <main.thing object at 0x10ff4b4a8>]
<main.thing object at 0x10ff4b5c0> [1, 1, 1, 1, 1] [<main.thing object at 0x10ff4b5f8>, <main.thing object at 0x10ff4b978>, <main[=191= .thing object at 0x10ff4b4e0>, <main.thing object at 0x10ff4b4a8>, <main.thing object at 0x10ff4b5c0>]
<main.thing object at 0x10ff4b588> [1, 1, 1, 1, 1, 1] [<main.thing object at 0x10ff4b5f8>, <main.thing object at 0x10ff4b978>, <main[=191= .thing object at 0x10ff4b4e0>, <main.thing object at 0x10ff4b4a8>, <main.thing object at 0x10ff4b5c0>, <main.thing object at 0x10ff4b588>]
<main.thing object at 0x10ff4b6d8> [1, 1, 1, 1, 1, 1, 1] [<main.thing object at 0x10ff4b5f8>, <main.thing object at 0x10ff4b978>, <main[=191= .thing object at 0x10ff4b4e0>, <main.thing object at 0x10ff4b4a8>, <main.thing object at 0x10ff4b5c0>, <main.thing object at 0x10ff4b588>, <main.thing object at 0x10ff4b6d8>]
<main.thing object at 0x10ff4b710> [1, 1, 1, 1, 1, 1, 1, 1] [<main.thing object at 0x10ff4b5f8>, <main.thing object at 0x10ff4b978>, <main[=191= .thing object at 0x10ff4b4e0>, <main.thing object at 0x10ff4b4a8>, <main.thing object at 0x10ff4b5c0>, <main.thing object at 0x10ff4b588>, <main.thing object at 0x10ff4b6d8>, <main.thing object at 0x10ff4b710>]
<main.thing object at 0x10ff4b668> [1, 1, 1, 1, 1, 1, 1, 1, 1] [<main.thing object at 0x10ff4b5f8>, <main.thing object at 0x10ff4b978>, <main[=191= .thing object at 0x10ff4b4e0>, <main.thing object at 0x10ff4b4a8>, <main.thing object at 0x10ff4b5c0>, <main.thing object at 0x10ff4b588>, <main.thing object at 0x10ff4b6d8>, <main.thing object at 0x10ff4b710>, <main.thing object at 0x10ff4b668>]
<main.thing object at 0x10ff4b748> [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] [<main.thing object at 0x10ff4b5f8>, <main.thing object at 0x10ff4b978>, <main[=191= .thing object at 0x10ff4b4e0>, <main.thing object at 0x10ff4b4a8>, <main.thing object at 0x10ff4b5c0>, <main.thing object at 0x10ff4b588>, <main.thing object at 0x10ff4b6d8>, <main.thing object at 0x10ff4b710>, <main.thing object at 0x10ff4b668>, <main.thing object at 0x10ff4b748>]
在函数块中实例化列表时,它每次都会创建一个新列表。
下面是使用 class 时的输出。
class Thing:
__ init__(self, stuff):
if stuff:
self.stuff = stuff
else:
self.stuff = []
for i in range(10):
obj = thing()
print(obj)
obj.stuff.append(1)
print(obj.stuff)
mylist.append(obj)
print(mylist)
输出
<main.thing object at 0x10ff4b518> [1] [<main.thing object at 0x10ff4b518>]
<main.thing object at 0x10ff4b4e0> [1] [<main.thing object at 0x10ff4b518>, <main.thing object at 0x10ff4b4e0>]
<main.thing object at 0x10ff4b6d8> [1] [<main.thing object at 0x10ff4b518>, <main.thing object at 0x10ff4b4e0>, <main[=191= .thing 对象在 0x10ff4b6d8>]
<main.thing object at 0x10ff4b550> [1] [<main.thing object at 0x10ff4b518>, <main.thing object at 0x10ff4b4e0>, <main[=191= .thing object at 0x10ff4b6d8>, <main.thing object at 0x10ff4b550>]
<main.thing object at 0x10ff4b7b8> [1] [<main.thing object at 0x10ff4b518>, <main.thing object at 0x10ff4b4e0>, <main[=191= .thing object at 0x10ff4b6d8>, <main.thing object at 0x10ff4b550>, <main.thing object at 0x10ff4b7b8>]
<main.thing object at 0x10ff4b7f0> [1] [<main.thing object at 0x10ff4b518>, <main.thing object at 0x10ff4b4e0>, <main[=191= .thing object at 0x10ff4b6d8>, <main.thing object at 0x10ff4b550>, <main.thing object at 0x10ff4b7b8>, <main.thing object at 0x10ff4b7f0>]
<main.thing object at 0x10ff4b668> [1] [<main.thing object at 0x10ff4b518>, <main.thing object at 0x10ff4b4e0>, <main[=191= .thing object at 0x10ff4b6d8>, <main.thing object at 0x10ff4b550>, <main.thing object at 0x10ff4b7b8>, <main.thing object at 0x10ff4b7f0>, <main.thing object at 0x10ff4b668>]
<main.thing object at 0x10ff4b6a0> [1] [<main.thing object at 0x10ff4b518>, <main.thing object at 0x10ff4b4e0>, <main[=191= .thing object at 0x10ff4b6d8>, <main.thing object at 0x10ff4b550>, <main.thing object at 0x10ff4b7b8>, <main.thing object at 0x10ff4b7f0>, <main.thing object at 0x10ff4b668>, <main.thing object at 0x10ff4b6a0>]
<main.thing object at 0x10ff4b748> [1] [<main.thing object at 0x10ff4b518>, <main.thing object at 0x10ff4b4e0>, <main[=191= .thing object at 0x10ff4b6d8>, <main.thing object at 0x10ff4b550>, <main.thing object at 0x10ff4b7b8>, <main.thing object at 0x10ff4b7f0>, <main.thing object at 0x10ff4b668>, <main.thing object at 0x10ff4b6a0>, <main.thing object at 0x10ff4b748>]
<main.thing object at 0x10ff4b828> [1] [<main.thing object at 0x10ff4b518>, <main.thing object at 0x10ff4b4e0>, <main[=191= .thing object at 0x10ff4b6d8>, <main.thing object at 0x10ff4b550>, <main.thing object at 0x10ff4b7b8>, <main.thing object at 0x10ff4b7f0>, <main.thing object at 0x10ff4b668>, <main.thing object at 0x10ff4b6a0>, <main.thing object at 0x10ff4b748>, <main.thing object at 0x10ff4b828>]
这是 Python 的怪癖。参数的默认值仅在定义函数时构造一次。所以,你会得到这个奇怪的行为:
def f(x=[]):
x.append('x')
return x
f()
# ['x']
f()
# ['x', 'x']
f()
# ['x', 'x', 'x']
将 []
替换为 list()
无济于事,因为在定义函数时它仍然只会被计算一次。然后,每次调用该函数时,相同的对象将作为默认对象重用。
要解决这个问题,您需要使用哨兵,然后明确测试它是否已被覆盖。这是一个常见的模式:
def f(x=None):
if x is None:
x = []
x.append('x')
return x
f()
# ['x']
f()
# ['x']
f(['a', 'b'])
# ['a', 'b', 'x']