__setitem__ 在 Python 中为 Point(x,y) 类实现

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15774804/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 20:58:33  来源:igfitidea点击:

__setitem__ implementation in Python for Point(x,y) class

pythonpython-2.7

提问by John Berry

I'm trying to make a Point class in python. I already have some of the functions, like __ str__ , or __ getitem__ implemented, and it works great. The only problem I'm facing is that my implementation of the __ setitem__ does not work, the others are doing fine.

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

Here is my Point class, and the last function is my __ 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

it should work like this:

它应该像这样工作:

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

(Am I even right, should the setitem work like this?) Thanks!

(我什至是对的,setitem 应该像这样工作吗?)谢谢!

采纳答案by unutbu

Let self.dataand only self.datahold the coordinate values. If self.xand self.ywere to also store these values there is a chance self.dataand self.xor self.ywill not get updated consistently.

self.data且只self.data保存坐标值。如果self.x并且self.y还要存储这些值,则有可能self.data并且self.xself.y不会一致地更新。

Instead, make xand ypropertiesthat look up their values from self.data.

相反,要xy性质,从查找它们的值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]


The statement

该声明

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

is interesting but problematic. Let pick it apart:

很有趣但有问题。让我们拆开看看:

[self.x, self.y]causes Python to build a new list, with values self.xand self.y.

[self.x, self.y]导致 Python 构建一个新列表,其中包含值self.xself.y

somelist[x]=ycauses Python to assign value yto the xth index of somelist. So this new list somelistgets updated. But this has no effect on self.data, self.xor self.y. That is why your original code was not working.

somelist[x]=y使 Python 为yx第 th 个索引赋值somelist。所以这个新列表somelist得到更新。但这对self.data,self.x或没有影响self.y。这就是为什么您的原始代码不起作用的原因。

回答by abarnert

Let's strip this down to the bare minimum:

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

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

This will print out 2.

这将打印出来2

Why?

为什么?

Well, [x, y]is a brand-new list containing two elements. When you do reassign its first member to 1, that just changes the brand-new list, so its first element is now 1instead of 2. It doesn't turn the number 2into the number 1.

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

Since your code is essentially identical to this, it has the same problem. As long as your variables have immutable values, you can't mutate the variables.

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



You couldfix it by doing something like this:

可以通过执行以下操作修复它:

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

Now you'll get 1.

现在你会得到1.

Why? Well, [x, y]is a new list with two elements, each of which is a list. You're not replacing its first element with something else, you're replacing the first element of its first element with something else. But its first element is the same list as x, so you're also replacing x's first element with something else.

为什么?嗯,[x, y]是一个包含两个元素的新列表,每个元素都是一个列表。你不是用别的东西替换它的第一个元素,你是用别的东西替换它的第一个元素的第一个元素。但是它的第一个元素与 相同x,因此您还用x其他元素替换了的第一个元素。



If this is a bit hard to keep straight in your head… well, that's usually a sign that you're doing something you probably shouldn't be. (Also, the fact that you're using xfor a parameter that means "select xor y" and yfor a parameter that means "new value" makes it a whole lot more confusing…)

如果这有点难以在你的脑海中保持清晰......好吧,这通常表明你正在做一些你可能不应该做的事情。(此外,您使用x的参数表示“选择xy”,y而参数表示“新值”的事实使它变得更加混乱……)

There are many simpler ways to do the same thing:

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

  • Use an if/elsestatement instead of trying to get fancy.
  • Use a single listinstead of two integer values: self.values[x] = y. (That's unutbu's answer.)
  • Use a dictinstead of two integer values: self.values['xy'[x]] = y.
  • Use setattr(self, 'xy'[x], y).
  • Use a namedtupleinstead of trying to build the same thing yourself.
  • 使用if/else语句而不是试图花哨。
  • 使用单个list而不是两个整数值:self.values[x] = y. (这是 unutbu 的答案。)
  • 使用 adict而不是两个整数值:self.values['xy'[x]] = y.
  • 使用setattr(self, 'xy'[x], y).
  • 使用 anamedtuple而不是尝试自己构建相同的东西。

回答by Michael Pratt

What's happening in setitem is it builds a temporary list, sets the value, then throws away this list without changing self.x or self.y. Try this for __setitem__:

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

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

This is quite an abuse of __setitem__, however... I'd advise figuring out a different way of setting the x/y coordinates if possible. Using p.x and p.y is going to be much faster than p[0] and p[1] pretty much no matter how you implement it.

__setitem__然而,这是对 的滥用......如果可能的话,我建议找出一种不同的方式来设置 x/y 坐标。无论您如何实现,使用 px 和 py 都将比 p[0] 和 p[1] 快得多。

回答by jBrushFX

This works in python 2.6 i guess it works for 2.7 as well

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

The __setitem__method accept 3 arguments (self, index, value)

__ setitem__方法接受3个参数(自,索引值)

in this case we want to use index as int for retrive the name of the coordinate from __slots__tuple (check the documentation of __slots__is really usefull for performance)

在这种情况下,我们想使用 index as int 来从__ slot__tuple检索坐标的名称(检查__ slot__的文档对性能非常有用)

remember with __slots__only xand yattributes are allowed! so:

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

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

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

This way is faster respect using @propertydecorator (when you start to have 10000+ instances)

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

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

so this is my tip for you :)

所以这是我给你的提示:)

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

回答by theodox

Your may find it a lot easier to use namedtuple for this:

您可能会发现使用 namedtuple 更容易:

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

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

The main drawback is that you can't poke values into a namedtuple - it's immutable. In most applications that's a feature, not a bug

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

回答by Gregory

This is pretty old post, but the solution for your problem is very simple:

这是很老的帖子,但解决您的问题很简单:

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

Each class has his own dictionary with all properties and methods created inside the class. So with this you can call:

每个类都有自己的字典,其中包含在类中创建的所有属性和方法。所以有了这个,你可以打电话:

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)

It is more readable then your "index" like reference.

它比您的“索引”之类的参考更具可读性。

回答by SeasonalShot

Here's an example:

下面是一个例子:

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]

If you don't use the __setitem__(), you will get an error

如果你不使用__setitem__(),你会得到一个错误

TypeError: 'FrenchDeck' object does not support item assignment