我正在尝试在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像这样工作吗?) 谢谢!
答案 0 :(得分:7)
让self.data
和self.data
保持坐标值。
如果self.x
和self.y
也存储了这些值,则self.data
和self.x
或self.y
将无法一致地更新。
相反,请x
和y
properties从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.x
和self.y
。
somelist[x]=y
会导致Python将值y
分配给x
的{{1}}索引。因此,此新列表somelist
会更新。但这对somelist
,self.data
或self.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
表示“选择x
或y
”的参数和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