List of Python objects with random attributes

(Edit: randrange is just random.randrange, I didn't write my own RNG)

I am trying to create a list of instances of a specific class. Here's the whole class (as requested):

from random import randrange

class Poly:
    points = [0] * 8
    fill = 'red'
    alpha = 1.0

    def __init__(self, width=100, height=100):
        for i in range(0, 8, 2):
            self.points[i] = randrange(width)
            self.points[i+1] = randrange(height)
        self.alpha = random()
        return

      

Seems to work well:

>>> for i in range(5):
        Poly().points

[28, 64, 93, 26, 15, 31, 44, 50]
[24, 14, 47, 14, 35, 17, 63, 62]
[99, 28, 90, 29, 56, 59, 57, 33]
[62, 56, 48, 28, 40, 73, 70, 99]
[99, 32, 27, 99, 42, 57, 86, 12]

      

But if I try to create a list of these objects, I get separate instances (different memory addresses), but they all have the same random values:

>>> p = []
>>> for i in range(5):
        p.append(Poly())

>>> p
[<gen_image.Poly instance at 0x02D773C8>, <gen_image.Poly instance at 0x02D77FD0>, <gen_image.Poly instance at 0x0321D030>, <gen_image.Poly instance at 0x02D51E40>, <gen_image.Poly instance at 0x02D51DA0>]

>>> for poly in p:
        print poly.points

[75, 18, 5, 76, 6, 64, 95, 54]
[75, 18, 5, 76, 6, 64, 95, 54]
[75, 18, 5, 76, 6, 64, 95, 54]
[75, 18, 5, 76, 6, 64, 95, 54]
[75, 18, 5, 76, 6, 64, 95, 54]

      

What's going on here? And what's the correct way to do what I am trying to do?

+1


a source to share


4 answers


Move array creation to method __init__

.

You are working with a common array among all objects.

The reason the former shows the other is because you are printing the contents of that array before you create a new Poly object and thus trample the contents of the array. If you saved them and checked them later, they all seem to have the same content as the previous one.

Oh, and try not to simplify your code when submitting questions. Always submit complete, but short, programs that reproduce the problem.

Here is a short but complete program that demonstrates the problem you are facing:

from random import randrange
class Poly:
    points = [0]*8

    def __init__(self, width=100, height=100):
        for i in range(0, 8, 2):
            self.points[i] = randrange(width)
            self.points[i+1] = randrange(height)
        return

p1 = Poly()
print "p1:", p1.points
p2 = Poly()
print "p2:", p2.points
print "p1:", p1.points

      



Output example:

[C:\Temp] test.py
p1: [19, 5, 1, 46, 93, 18, 18, 57]
p2: [92, 71, 42, 84, 54, 29, 27, 71]
p1: [92, 71, 42, 84, 54, 29, 27, 71]

      

Notice how p1 has changed.

The fixed code can be as simple as:

from random import randrange
class Poly:
    def __init__(self, width=100, height=100):
        self.points = [0]*8
        for i in range(0, 8, 2):
            self.points[i] = randrange(width)
            self.points[i+1] = randrange(height)
        return

      

although I prefer the add option that @Doug posted here

+6


a source


You have a class attribute Poly.points

. In your method, __init__

you are executing self.points[i] = ...

. This now makes Python use Poly.points

that is used by all instances. But you want to points

be an instance attribute. I would suggest the following:



class Poly:
    # you don't need this here
    #points = [0] * 8
    #fill = 'red'
    #alpha = 1.0

    def __init__(self, width=100, height=100):
        self.points = [0]*8
        self.fill = 'red'
        self.alpha = random()
        for i in range(0, 8, 2):
            self.points[i] = randrange(width)
            self.points[i+1] = randrange(height)

      

+4


a source


All lists of shared lists. It would seem that you are declaring points as a list of an instance or class. This is not the way to do things in Python unless you want to share the list between instances. Try:


def __init__(self, width=100, height=100):
    self.points = [] #Create a new list
    for i in range(0, 8, 2):
        self.points.append(randrange(width))
        self.points.append(randrange(height))
    return


      

+2


a source


ok here is the culprit

points = [[0]] * 8

it assigns the same list ([0]) 8 times instead you should do something like

points = []
for i in range(8):
    points.append([])

      

-1


a source







All Articles