2013-04-02 49 views
2

我試圖在python中創建一個Point類。我已經有了一些函數,比如__ str__或__ getitem__,它很好用。 我面臨的唯一問題是,我的__setitem__實現不起作用,其他人都做得很好。Python中爲Point(x,y)實現的__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

豈不是self.data.x? – karthikr

+0

你試過了嗎?它有什麼作用? – abarnert

回答

5

self.data和只有self.data保存座標值。 如果self.xself.y也存儲這些值有機會self.dataself.xself.y不會得到一致更新。

取而代之,使xypropertiesself.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指數somelist。所以這個新名單somelist得到更新。但是這對self.data,self.xself.y沒有影響。這就是爲什麼你的原始代碼不工作。

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而不是試圖自己構建相同的東西。
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]快得多。

2

這在python 2.6中工作,我猜它適用於2。7以及

__ setitem __方法接受3個參數(自我,指數值)

在這種情況下,我們要使用指數作爲INT爲retrive從座標名字__ 插槽 __元組(檢查__ 插槽 __的文檔非常有用的性能)

記得__ 插槽 __只有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 
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) 

主要缺點是您無法將值戳入namedtuple - 它是不可變的。在大多數應用中,這是一個功能,而不是一個錯誤

1

這是很老的文章,但對你的問題的解決方案很簡單:

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) 

它更可讀然後你的「索引」就像參考。

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 
相關問題