3

Let's say we have a Foo class and we want to instantiate a lists of foos, which we can do as follows:

foo_list = [Foo()] * 3

Now what if we do foo_list[0].x = 5? This statement will set the attribute x to all of the instances inside the list!!!

When I found this it blew my mind. The problem is that when we create a list like that, apparently python will create the Foo instance first, then the list instance, and then append the same object to it 3 times, but I really would expect it to do something like this:

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

Wouldn't you too? Well, now I know that definitely this syntactic sugar can't be used as I wanted to use it, but then, what is it used for? The only thing I can think of is to create a list with an initial size like this: list = [None] * 5, and that doesn't make much sense to me in the case of python.

Does this syntax have any other really useful use case?

educampver
  • 2,967
  • 2
  • 23
  • 34
  • 1
    When working with immutable objects, reusing objects can save quite a lot of memory. – Joachim Isaksson Dec 23 '13 at 07:59
  • 1
    I thought I'd mention this little gem, which is rather handy for visualising when you're getting copies or references: [python visualizer](http://www.pythontutor.com/visualize.html). – Rob Dec 23 '13 at 08:16

3 Answers3

7

You can use the star form, with any immutable type, like this

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

Lets say, for example

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

Output on my machine

[41266184, 41266184, 41266184]

id function gives a unique id of the current object. As you can see, creating immutable objects this way is very simple, and efficient. Because all the elements in the created object, point to the same element. (Remember the objects used are immutable)

So, as a rule of thumb,

if the objects are mutable, use list comprehension form

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

if the objects are immutable, use can use the star form

[5] * 3
thefourtheye
  • 233,700
  • 52
  • 457
  • 497
  • Ok, I forgot about inmutable objects, but wouldn't it be confusing or even problematic when used on class objects? – educampver Dec 23 '13 at 08:03
  • 1
    @ecampver As class objects are mutable, Yes. This will create confusions. – thefourtheye Dec 23 '13 at 08:04
  • Wow, didn't know that last part, thanks!! I've just checked that the same happens with strings. Could I assume that this happens with any immutable object? – educampver Dec 23 '13 at 08:11
  • @ecampver You are correct :) The behavior is same with any immutable object. – thefourtheye Dec 23 '13 at 08:13
  • 1
    Using `nums=[];nums.append(5);nums.append(5);nums.append(5);print map(id,nums);` gives us the same result. So, I think it's the python interpreter not the operator* detects the same immutable object and use the same reference. – WKPlus Dec 23 '13 at 08:16
  • @WKPlus You are correct, it has nothing to with the `*` operator. It simply multiplies the element and returns the result. Thats it. The behavior depends on the input object which we choose. If that is immutable, all are good. If not, we ll end up altering all the other objects. – thefourtheye Dec 23 '13 at 08:18
0

It's not syntactic sugar at all, it's just multiplying a list. That is,

foo_list = [Foo()] * 3

is

foo_list = ( [Foo()] ) * 3

is

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

No special syntax at all. It creates a list that has the same contents as the list being multiplied, three times.

One thing I use it for is when I get a list of unknown length and I want it to have exactly length n, then I add a list of n times a default value to it, then slice:

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

Of course doing that with mutable defaultvalues leads to trouble, as you've found.

RemcoGerlich
  • 30,470
  • 6
  • 61
  • 79
0

The syntax:

list=[any_object]*n

is a short-cut of making a list of n same objects.As you can see the following:

>>> 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>]

In the above code the three objects are same in the

list l

as indicated by there memory locations or you can use id(object) but in the

list m

they are different. The point is when you use list comprehensions you actually do the process of creating three objects in one line rather than using a loop to instantiate objects and putting them in a list,but the former list=[any_object]*nis just again a shotcut of appending the same object in a list desired number of times.

Community
  • 1
  • 1
darxtrix
  • 2,032
  • 2
  • 23
  • 30