具有随机属性的Python对象列表

2 投票
4 回答
3683 浏览
提问于 2025-04-15 11:38

(编辑:randrange其实就是random.randrange,我并没有自己写随机数生成器)

我正在尝试创建一个我定义的类的实例列表。以下是整个类的代码(应要求提供):

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

看起来运行得很好:

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

但是如果我尝试创建这些对象的列表,我得到的是不同的实例(内存地址不同),但它们的随机值都是一样的:

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

这是怎么回事?我该怎么做才能实现我想要的效果?

4 个回答

2

看起来你在实例或类中把点(points)声明成了一个共享的列表。这种做法在Python中并不推荐,因为这样会导致不同的实例之间共享这个列表。如果你不想让这些实例之间共享列表,可以试试下面的方法:


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

4

你有一个类属性叫做 Poly.points。在你的 __init__ 方法里,你写了 self.points[i] = ...。这样做会让 Python 使用 Poly.points,这个属性是所有实例共享的。但是你想要 points 成为一个实例属性。我的建议是这样做:

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

把数组的创建放到 __init__ 方法里。

你现在在所有对象之间共享一个数组。

第一个例子显示的内容不同,是因为你在创建新的 Poly 对象之前就打印了那个数组的内容,这样就覆盖了数组里的内容。如果你把它们保留下来,稍后再查看,它们都会显示和你最后生成的那个对象一样的内容。

哦,还有,发问题的时候尽量不要简化代码。总是要发完整但简短的程序,这样才能重现问题。

下面是一个简短但完整的程序,展示了你遇到的问题:

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

示例输出:

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

注意 p1 是怎么变化的。

修正后的代码可以简单到:

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

不过我更喜欢 @Doug这里 发的那种追加方式。

撰写回答