[Tutor] is this use or abuse of __getitem__ ?
eryksun
eryksun at gmail.com
Sat Sep 15 10:43:25 CEST 2012
On Fri, Sep 14, 2012 at 2:33 PM, Albert-Jan Roskam <fomcl at yahoo.com> wrote:
>> On 14/09/12 22:16, Albert-Jan Roskam wrote:
>
> Is it recommended to define the geitem() function inside the __getitem__() method?
> I was thinking I could also define a _getitem() private method.
>
> def getitem(key):
> retcode1 = self.iomodule.SeekNextCase(self.fh, ctypes.c_long(int(key)))
> ....
I wouldn't do this since it incurs the cost of a repeated function
call. A slice could involve thousands of such calls. Maybe use a
boolean variable like "is_slice". Then use a for loop to build the
records list (maybe only 1 item). If is_slice, return records, else
return records[0].
> if isinstance(key, slice):
> records = [getitem(i) for i in range(*key.indices(self.nCases))]
> return records
> elif hasattr(key, "__int__"): # isinstance(key, (int, float)):
> if abs(key) > (self.nCases - 1):
> raise IndexError
> else:
> key = self.nCases + key if key < 0 else key
> record = getitem(key)
> return record
> else:
> raise TypeError
I agree with Steven's reasoning that it doesn't make sense to support
floating point indexes. Python 2.6+ has the __index__ special method.
int and long have this method. float, Decimal,and Fraction do not have
it. It lets you support any user-defined class that can be used as an
index. For example:
>>> class MyInt(object):
... def __index__(self):
... return 5
>>> slice(MyInt(), MyInt(), MyInt()).indices(10)
(5, 5, 5)
operator.index() is the corresponding function. It raises TypeError if
__index__ isn't supported.
But watch out because you're using ctypes.c_long. It doesn't do any
range checking. It just silently wraps around modulo the size of a
long on your platform:
>>> c_long(2**32-1), c_long(2**32), c_long(2**32+1)
(c_long(-1), c_long(0), c_long(1))
Calling int(key) or index(key) is no help because it will silently
return a Python long (big int). You need to do range checking on the
upper bound and raise a ValueError.
For example:
from operator import index # calls obj.__index__()
is_slice = isinstance(key, slice)
if is_slice:
start, stop, step = key.indices(self.nCases) # may raise TypeError
else:
start = index(self.nCases + key if key < 0 else key) # may
raise TypeError
stop = start + 1
step = 1
if stop > 2 ** (ctypes.sizeof(ctypes.c_long) * 8 - 1):
raise ValueError('useful message')
records = []
for i in range(start, stop, step):
retcode1 = self.iomodule.SeekNextCase(self.fh, ctypes.c_long(i))
self.caseBuffer, self.caseBufferPtr = self.getCaseBuffer()
retcode2 = self.iomodule.WholeCaseIn(self.fh, self.caseBufferPtr)
record = struct.unpack(self.structFmt, self.caseBuffer.raw)
if any([retcode1, retcode2]):
raise RuntimeError("Error retrieving record %d [%s, %s]" %
(i, retcodes[retcode1], retcodes[retcode2]))
records.append(record)
if not is_slice:
records = records[0]
return records
More information about the Tutor
mailing list