Initialize a list of objects in Python

61,134

Solution 1

There isn't a way to implicitly call an Object() constructor for each element of an array like there is in C++ (recall that in Java, each element of a new array is initialised to null for reference types).

I would say that your list comprehension method is the most Pythonic:

lst = [Object() for i in range(100)]

If you don't want to step on the lexical variable i, then a convention in Python is to use _ for a dummy variable whose value doesn't matter:

lst = [Object() for _ in range(100)]

For an equivalent of the similar construct in Java, you can of course use *:

lst = [None] * 100

Solution 2

You should note that Python's equvalent for Java code (creating array of 100 null references to Object):

Object arr = new Object[100];

or C++ code:

Object **arr = new Object*[100];

is:

arr = [None]*100

not:

arr = [Object() for _ in range(100)]

The second would be the same as Java's:

Object arr = new Object[100];
for (int i = 0; i < arr.lenght; i++) {
    arr[i] = new Object();
}

In fact Python's capabilities to initialize complex data structures are far better then Java's.


Note: C++ code:

Object *arr = new Object[100];

would have to do as much work as Python's list comprehension:

  • allocate continuous memory for 100 Objects

  • call Object::Object() for each of this Objects

And the result would be a completely different data structure.

Solution 3

I think the list comprehension is the simplest way, but, if you don't like it, it's obviously not the only way to obtain what you desire -- calling a given callable 100 times with no arguments to form the 100 items of a new list. For example, itertools can obviously do it:

>>> import itertools as it
>>> lst = list(it.starmap(Object, it.repeat((), 100)))

or, if you're really a traditionalist, map and apply:

>>> lst = map(apply, 100*[Object], 100*[()])

Note that this is essentially the same (tiny, both conceptually and actually;-) amount of work it would take if, instead of needing to be called without arguments, Object needed to be called with one argument -- or, say, if Object was in fact a function rather than a type.

From your surprise that it might take "as much as a list comprehension" to perform this task, you appear to think that every language should special-case the need to perform "calls to a type, without arguments" over other kinds of calls to over callables, but I fail to see what's so crucial and special about this very specific case, to warrant treating it differently from all others; and, as a consequence, I'm pretty happy, personally, that Python doesn't single this one case out for peculiar and weird treatment, but handles just as regularly and easily as any other similar use case!-)

Share:
61,134
bradreaves
Author by

bradreaves

Updated on August 28, 2020

Comments

  • bradreaves
    bradreaves almost 4 years

    I'm a looking to initialize an array/list of objects that are not empty -- the class constructor generates data. In C++ and Java I would do something like this:

    Object lst = new Object[100];
    

    I've dug around, but is there a Pythonic way to get this done?

    This doesn't work like I thought it would (I get 100 references to the same object):

    lst = [Object()]*100
    

    But this seems to work in the way I want:

    lst = [Object() for i in range(100)]
    

    List comprehension seems (intellectually) like "a lot" of work for something that's so simple in Java.

  • inspectorG4dget
    inspectorG4dget over 14 years
    I feel that you are overcomplicating things. Something as simple as this will NOT require the import of other modules
  • bradreaves
    bradreaves over 14 years
    Good point about the special case -- I guess I hadn't thought about it from that point of view. That map line is really neat -- I haven't played with maps yet, so I'll have to play with it.
  • Alex Martelli
    Alex Martelli over 14 years
    Ah but @Inspector, you see, the map/apply solution doesn't require any import, but that doesn't make it any less complicated than the itertools one: it just happens to use old stuff which happened to be placed in builtins many, many years ago, while itertools is a newer idea and was properly put in its own module of the standard library. apply is gone in Python 3, and map changed. So, you see: your perceptions of what's complicated (based on such a totally inappropriate criterion as using import or not!) are really entirely, fundamentally wrong and misplaced.
  • Serge Stroobandt
    Serge Stroobandt over 8 years
    @orip In Python 3, range() does what xrange() used to do and xrange() does not exist. If you want to write code that will run on both Python 2 and Python 3, you cannot use xrange().
  • orip
    orip over 8 years
    @SergeStroobandt fair enough, although there are more significant differences between 2 and 3 and I'd suggest a different approach for compatibility, e.g target 3 explicitly or maintain compatibility with 2to3, instead of surprise Python2 users with potentially huge memory allocations
  • Pete P
    Pete P about 7 years
    it's interesting that in my Eclipse environment, the top list comprehension way was giving me warnings about the unused variable i and when I used the underscore, it stopped warning me about this. I'm wondering, does it actually change what happens? Does Python know the underscore is a dummy variable that is unused and therefore perform faster?
  • Greg Hewgill
    Greg Hewgill about 7 years
    @PeteP: No, using the underscore does not change what Python does. (You can check this yourself by timing the code with and without the underscore.) You're just seeing that using the underscore changes the behaviour of Eclipse.