11

我正在尝试在 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 应该像这样工作吗?)谢谢!

4

7 回答 7

16

self.data并且只self.data保留坐标值。如果self.x并且self.y还要存储这些值,则有机会self.data并且self.xself.y不会持续更新。

相反,makexy 属性self.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给. 所以这个新列表得到更新。但这对或没有影响。这就是为什么您的原始代码不起作用的原因。xsomelistsomelistself.dataself.xself.y

于 2013-04-02T21:37:24.007 回答
2

让我们把它精简到最低限度:

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

这将打印出来2

为什么?

嗯,[x, y]是一个包含两个元素的全新列表。当您将其第一个成员重新分配给1时,只会更改全新的列表,因此它的第一个元素现在12. 它不会把 number2变成 number 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的答案。)
  • 使用 adict而不是两个整数值:self.values['xy'[x]] = y.
  • 使用setattr(self, 'xy'[x], y).
  • 使用 anamedtuple而不是尝试自己构建相同的东西。
于 2013-04-02T21:41:00.887 回答
2

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

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

在这种情况下,我们想使用 index 作为 int 从__ slots __ tuple 中检索坐标的名称(检查__ slots __的文档对性能非常有用)

记住__插槽__只允许xy属性!所以:

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
于 2013-08-21T22:10:24.037 回答
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)

它比你的“索引”更易读,比如参考。

于 2014-05-11T10:15:11.943 回答
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)

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

于 2013-08-21T22:15:55.217 回答
0

setitem 中发生的事情是它构建一个临时列表,设置值,然后在不更改 self.x 或 self.y 的情况下丢弃该列表。试试这个__setitem__

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

但是,这完全是对__setitem__. 无论您如何实现,使用 px 和 py 都将比 p[0] 和 p[1] 快得多。

于 2013-04-02T21:41:37.190 回答
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
于 2016-07-08T05:42:46.660 回答