1

I'm using Python 2.7 and I ran into the following situation:

I have a Polygon object that consists of N vertices stored in a list. I have abstracted out a function rand_xy() that generates a random point to be a vertex, and I would like to create Polygon objects with N random vertices.

I tried this:

vertices = [ rand_xy() ] * N

but it makes a list of N vertices that are all the same. And I guess it makes sense because the call to rand_xy() is evaluated first, and then it is duplicated N times.

I wish it would instead make the list by calling rand_xy() N times. Is there any way to accomplish this (preferably in one line)?

I realized that the "obvious" way is to just do this:

vertices = []
for i in range( 0 , N ):
    vertices.append( rand_xy() )

but my Polygon constructor is

def __init__( self , vertices , color )

and I'd really like it if I could just set the default value of vertices to be a random list of N points like this:

def __init__( self , vertices = [ rand_xy() ] * N , color = rand_rgb() )

This way, calling the Polygon constructor with no arguments will give me a random new Polygon. Or is there a better design choice for the constructor? Or can this just not be done in Python?

Thanks for the help!

user2570465
  • 2,437
  • 2
  • 18
  • 22

2 Answers2

2

Yes, as you observed, when you do - [ rand_xy() ] * N - first the function call is evaluated and then the result in put in the list and then multiplied to create a list of N elements (though all same items).

You can do list comprehension instead of [ rand_xy() ] * N -

vertices = [rand_xy() for _ in range(N)]

This can be done in a single line.


But please note, you should not use this or any list as a default argument. I am guessing when you say you need to do -

def __init__( self , vertices = [ rand_xy() ] * N , color = rand_rgb() )

This means you are trying to use it as default argument. But please note, default arguments are only evaluated once, when the function is defined , it is not evaluated everytime the function is called without arguments. Check the question - "Least Astonishment" and the Mutable Default Argument - for more details.

Instead of that, you can use None or so and then create the default values inside the function body, if the values are None. Example -

def __init__( self , vertices = None , color = rand_rgb() )
    if vertices is None:
        vertices = [rand_xy() for _ in range(N)]

You can do same for color , if you want it to return a different random rgb value everytime you clal the function without arguments.

Community
  • 1
  • 1
Anand S Kumar
  • 88,551
  • 18
  • 188
  • 176
1

to initialize your vertices to a default random value, you could do like this:

def __init__( self , vertices = None , color = rand_rgb() ):
    self.vertices = [rand_xy() for _ in range(N)] if vertices is None else vertices

It is not a good idea to assign default values to lists or other mutable structures.

Reblochon Masque
  • 35,405
  • 10
  • 55
  • 80