【问题标题】:Star operator (*) applied to lists and integers [duplicate]星号运算符 (*) 应用于列表和整数 [重复]
【发布时间】:2013-12-23 07:55:45
【问题描述】:

假设我们有一个Foo 类,我们想实例化一个foos 的列表,我们可以这样做:

foo_list = [Foo()] * 3

现在如果我们使用foo_list[0].x = 5 会怎样?此语句会将属性x 设置为列表中的所有实例!!!

当我发现这个时,它让我大吃一惊。问题是,当我们创建这样的列表时,显然 python 将首先创建 Foo 实例,然后是列表实例,然后将相同的对象附加到它 3 次,但我真的希望它做这样的事情:

foo_list = [Foo() for i in range(3)]

你不也是吗?好吧,现在我知道了,这个语法糖肯定不能用我想用的,但是,它是干什么用的呢?我唯一能想到的就是创建一个初始大小如下的列表:list = [None] * 5,这对我来说在 python 的情况下没有多大意义。

这种语法还有其他真正有用的用例吗?

【问题讨论】:

  • 在使用不可变对象时,重用对象可以节省大量内存。
  • 我想我会提到这个小宝石,当您获得副本或参考资料时,它非常便于可视化:python visualizer

标签: python python-2.7


【解决方案1】:

你可以使用星型,任何不可变类型,像这样

print [5] * 3
print "abc" * 3
print [1.1] * 3
print (8,) * 3

比如说,例如

nums = [5] * 3
print map(id, nums)

在我的机器上输出

[41266184, 41266184, 41266184]

id 函数给出当前对象的唯一 ID。如您所见,以这种方式创建不可变对象非常简单且高效。因为创建的对象中的所有元素,都指向同一个元素。 (记住使用的对象是不可变的)

所以,根据经验法则

如果对象是可变的,则使用列表推导式

[Foo() for i in range(3)]

如果对象是不可变的,使用可以使用星型

[5] * 3

【讨论】:

  • 好的,我忘记了不可变对象,但是在类对象上使用它不会令人困惑甚至有问题吗?
  • @ecampver 因为类对象是可变的,是的。这会造成混乱。
  • 哇,不知道最后一部分,谢谢!!我刚刚检查过字符串也会发生同样的情况。我可以假设任何不可变对象都会发生这种情况吗?
  • @ecampver 你是对的 :) 任何不可变对象的行为都是一样的。
  • 使用nums=[];nums.append(5);nums.append(5);nums.append(5);print map(id,nums); 给我们同样的结果。所以,我认为是 python 解释器而不是 operator* 检测到相同的不可变对象并使用相同的引用。
【解决方案2】:

它根本不是语法糖,它只是将一个列表相乘。也就是说,

foo_list = [Foo()] * 3

foo_list = ( [Foo()] ) * 3

foo_instance = Foo()
bar_list = [foo_instance]
foo_list = bar_list * 3

根本没有特殊的语法。它创建一个列表,该列表的内容与被乘以三倍的列表相同。

我使用它的一件事是当我得到一个长度未知的列表并且我希望它的长度正好为 n 时,然后我添加一个 n 乘以默认值的列表,然后切片:

def set_to_length(l, n, defaultvalue=0):
    return (l + [defaultvalue] * n)[:n]

当然,使用可变的默认值这样做会导致麻烦,正如您所发现的那样。

【讨论】:

    【解决方案3】:

    语法:

    list=[any_object]*n
    

    是制作n个相同对象列表的快捷方式。如您所见:

    >>> l=[foo()]*3
    >>> l
    [<__main__.foo instance at 0x02D64D28>, <__main__.foo instance at 0x02D64D28>, <__main__.foo instance at 0x02D64D28>]
    >>> m=[ foo() for i in range(3)]
    >>> m
    [<__main__.foo instance at 0x02D64F08>, <__main__.foo instance at 0x02D613A0>, <__main__.foo instance at 0x02D611C0>]
    

    在上面的代码中,三个对象是一样的

    列表 l

    如内存位置所示,或者您可以使用 id(object) 但在

    列表 m

    它们是不同的。 关键是当您使用列表推导时,您实际上是在一行中创建三个对象的过程,而不是使用循环来实例化对象并将它们放入列表中,但前者 list=[any_object]*n 只是再次添加相同的快照列表中的对象所需的次数。

    【讨论】:

      猜你喜欢
      • 2011-12-26
      • 2020-08-20
      • 2013-08-04
      • 1970-01-01
      • 2014-03-03
      • 1970-01-01
      • 2012-05-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多