用于Point(x,y)类的Python中的__setitem__实现

时间:2013-04-02 21:32:10

标签: python python-2.7

我正在尝试在python中创建一个Point类。我已经有一些函数,比如__ str__或__ getitem__,它运行得很好。 我面临的唯一问题是我的__ setitem__的实现不起作用,其他人都做得很好。

这是我的Point类,最后一个函数是我的__ setitem __:

class point(object):
    def __init__(self,x=0,y=0):
        self.x=x
        self.y=y

    def __str__(self):
        return "point(%s,%s)"%(self.x,self.y)

    def __getitem__(self,item):
        return (self.x, self.y)[item]

    def __setitem__(self,x,y):
        [self.x, self.y][x]=y

它应该像这样工作:

p=point(2,3)
p[0]=1 #sets the x coordinate to 1
p[1]=10 #sets the y coordinate to 10

(我甚至是对的,如果setitem像这样工作吗?) 谢谢!

7 个答案:

答案 0 :(得分:7)

self.dataself.data保持坐标值。 如果self.xself.y也存储了这些值,则self.dataself.xself.y将无法一致地更新。

相反,请xy propertiesself.data查找其值。

class Point(object):
    def __init__(self,x=0,y=0):
        self.data=[x, y]

    def __str__(self):
        return "point(%s,%s)"%(self.x,self.y)

    def __getitem__(self,item):
        return self.data[item]

    def __setitem__(self, idx, value):
        self.data[idx] = value

    @property
    def x(self):
        return self.data[0]

    @property
    def y(self):
        return self.data[1]

声明

[self.x, self.y][x]=y

很有趣,但有问题。让它分开:

[self.x, self.y]会导致Python构建新列表,其值为self.xself.y

somelist[x]=y会导致Python将值y分配给x的{​​{1}}索引。因此,此新列表somelist会更新。但这对somelistself.dataself.x没有影响。这就是原始代码无效的原因。

答案 1 :(得分:2)

这适用于python 2.6我猜它也适用于2.7

__ setitem __ 方法接受3个参数(self,index,value)

在这种情况下,我们希望使用index作为int来从 __ slots __ 元组中检索坐标的名称(查看 __ 插槽 __ 对于性能非常有用)

请记住 __ 广告位 __ 仅允许 x y 属性!这样:

p = Point()
p.x = 2
print(p.x)  # 2.0

p.z = 4  # AttributeError
print(p.z)  # AttributeError

这种方式使用 @property 装饰器(当你开始拥有10000多个实例时)更快的尊重

class Point(object):
    @property
    def x(self):
        return self._data[0]  # where self._data = [x, y]
...

所以这是我的提示:)

class Point(object):

    __slots__ = ('x', 'y')  # Coordinates

    def __init__(self, x=0, y=0):
        '''
        You can use the constructor in many ways:
        Point() - void arguments
        Point(0, 1) - passing two arguments
        Point(x=0, y=1) - passing keywords arguments
        Point(**{'x': 0, 'y': 1}) - unpacking a dictionary
        Point(*[0, 1]) - unpacking a list or a tuple (or a generic iterable)
        Point(*Point(0, 1)) - copy constructor (unpack the point itself)
        '''
        self.x = x
        self.y = y

    def __setattr__(self, attr, value):
        object.__setattr__(self, attr, float(value))

    def __getitem__(self, index):
            '''
            p = Point()
            p[0]  # is the same as self.x
            p[1]  # is the same as self.y
            '''
        return self.__getattribute__(self.__slots__[index])

    def __setitem__(self, index, value):
            '''
            p = Point()
            p[0] = 1
            p[1] = -1
            print(repr(p))  # <Point (1.000000, -1.000000)>
            '''
        self.__setattr__(self.__slots__[index], value)  # converted to float automatically by __setattr__

    def __len__(self):
        '''
        p = Point()
        print(len(p))  # 2
        '''
        return 2

    def __iter__(self):
        '''
        allow you to iterate
        p = Point()
        for coord in p:
            print(coord)

        for i in range(len(p)):
            print(p[i])
        '''
        return iter([self.x, self.y])

    def __str__(self):
        return "(%f, %f)" % (self.x, self.y)

    def __repr__(self):
        return "<Point %s>" % self

答案 2 :(得分:2)

这是很老的帖子,但问题的解决方案非常简单:

class point(object):
    def __init__(self,x=0,y=0):
        self.x=x
        self.y=y

    def __str__(self):
        return "point(%s,%s)"%(self.x,self.y)

    def __getitem__(self,item):
        return self.__dict__[item]

    def __setitem__(self,item,value):
        self.__dict__[item] = value

每个类都有自己的字典,其中包含在类中创建的所有属性和方法。因此,您可以致电:

In [26]: p=point(1,1)
In [27]: print p
point(1,1)
In [28]: p['x']=2
In [29]: print p
point(2,1)
In [30]: p['y']=5
In [31]: print p
point(2,5)

它比你的&#34;索引更具可读性&#34;喜欢参考。

答案 3 :(得分:1)

让我们把它剥离到最低限度:

x, y = 2, 3
[x, y][0] = 1
print(x)

这将打印出2

为什么?

嗯,[x, y]是一个包含两个元素的全新列表。当您将其第一个成员重新分配给1时,这只会更改全新的列表,因此其第一个元素现在是1而不是2。它不会将数字2转换为数字1

由于您的代码与此基本相同,因此它存在同样的问题。只要您的变量具有不可变值,就不能改变变量。


可以通过这样做来修复它:

x, y = [2], [3]
[x, y][0][0] = 1
print(x[0])

现在你将获得1

为什么呢?好吧,[x, y]是一个包含两个元素的新列表,每个元素都是一个列表。你没有用其他东西替换它的第一个元素,你用其他东西替换它的第一个元素的第一个元素。但它的第一个元素与x的列表相同,所以你也用其他东西替换x的第一个元素。


如果这有点难以保持在你的头脑中......那么,这通常表明你正在做一些你可能不应该做的事情。 (另外,您使用x表示“选择xy”的参数和y表示“新值”的参数的事实它更令人困惑......)

有许多更简单的方法可以做同样的事情:

  • 使用if / else声明,而不是试图获得幻想。
  • 使用单个list而不是两个整数值:self.values[x] = y。 (这是unutbu的答案。)
  • 使用dict代替两个整数值:self.values['xy'[x]] = y
  • 使用setattr(self, 'xy'[x], y)
  • 使用namedtuple而非尝试自己构建相同的内容。

答案 4 :(得分:1)

你可能会发现使用namedtuple要容易得多:

 from collections import namedtuple
 Point= namedtuple('Point', ['x','y'])

 fred = Point (1.0, -1.0)
 #Result: Point(x=1.0, y=-1.0)

主要缺点是你不能将值戳到一个命名元组中 - 它是不可变的。在大多数应用程序中,这是一个功能,而不是错误

答案 5 :(得分:0)

setitem中发生的事情是它构建一个临时列表,设置值,然后抛弃此列表而不更改self.x或self.y.试试__setitem__

def __setitem__(self,coord,val):
    if coord == 0:
        self.x = val
    else:
        self.y = val

这是对__setitem__的滥用,但是......如果可能的话,我建议找出另一种设置x / y坐标的方法。使用p.x和p.y将比p [0]和p [1]快得多,无论你如何实现它。

答案 6 :(得分:0)

以下是一个例子:

from collections import namedtuple

Deck = namedtuple('cards',['suits','values'])

class FrenchDeck(object):

    deck = [str(i) for i in range(2,11)]+list('JQKA')
    suits = "heart clubs spades diamond".split()

    def __init__(self):
        self.totaldecks = [Deck(each,every) for each in self.suits for every in self.deck]
    def __len__(self):
        return len(self.totaldecks)
    def __getitem__(self,index):
        return self.totaldecks[index]
    def __setitem__(self,key,value):
        self.totaldecks[key] = value

CardDeck = FrenchDeck()
CardDeck[0] = "asdd"       # needs`__setitem__()`
print CardDeck[0]

如果您不使用__setitem__(),则会收到错误

TypeError: 'FrenchDeck' object does not support item assignment