[Types-sig] Another cut at Sequence declarations
Tim Hochberg
tim.hochberg@ieee.org
Wed, 14 Mar 2001 16:10:56 -0700
This is a multi-part message in MIME format.
------=_NextPart_000_0361_01C0ACA1.59A4F970
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
Attached is my attempt to write a set typecheck classes for classes. So far
I've only tried to deal with immutable sequence types; mutable ones are on
the agenda, but I've used up my allotment of types-sig time for the day.
Here's the executive summary, for the rest, see the code:
I defined three protocols: Iterable, BasicSequence and Sequence. Iterable is
something you can iterate on. Currently that means it supports getitem, but
this will be expanded to include whatever the iter-sig comes up with.
BasicSequence supports getitem and len. This is the minimum that you need to
construct an immutable sequence (e.g., a tuple). Sequence supports the same
set of operations as a tuple. So, Sequence extends BasicSequence which in
turn extends Iterable.
I've also included a base class for users that want to create their own
Sequences. This class is called SequenceBase. Only getitem and len must be
overriden to get a working Sequence. The combination of the names
BasicSequence and SequenceBase is probably bad, so at least one of them
should be changed.
Finally, there is a class SequenceWrapper that creates a Sequence out of a
BasicSequence. It's used as "aSequence=SequenceWrapper(aBasicSequence)" I'm
not sure how necessary this is since "tuple(aBasicSequence)" would work as
well or better in most situations.
All names are, of course, open for debate.
I think the general approach taken here and in Paul's file classes may be
useful elsewhere: take a canonical builtin type, in this case tuple for
immutable sequences. Define a wide protocol based on that, in this case
Sequence. Find the narrowest interface that allows you to recreate that
type, and define a protocol based on that, in this case BasicSequence.
Finally define some helpers that help derive classes that obey the wide
protocol and convert objects that obey the narrow protocol to the wide
protocol, in this case SequenceBase and SequenceWrapper. I'll try the same
with MutableSequences, starting with lists, and report back as to whether
this approach works there as well. Later though.
-tim
------=_NextPart_000_0361_01C0ACA1.59A4F970
Content-Type: text/plain;
name="sequence.py"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="sequence.py"
import types, operator
class _iterable:
"An iterable implements __getitem__ or the iteration protocol (TBD)"
def __implementedby__(self, object):
#if implements_iter_protocol(object): return 1
if hasattr(object, "__getitem__"): return 1
# This isn't really right, what we want to do is to check =
whether
# object[0] will work. However using the try: object[0] approach
# man not work correctly. If the object uses the
# current iteration protocol calling object[0] may
# advance the iteration.
if not type(object) =3D=3D types.InstanceType and \
operator.isSequenceType(object): return 1
return 0
Iterable =3D _iterable() =20
class _basicSequence:
# This is an iterable, but it doesn't seem useful to inherit
"""A basic sequence implements __getitem__ and __len__
This is sufficient to build a tuple, but doesn't necessarily
support concatenation, repitition or slicing.
"""
def __implementedby__(self, object):
try:
len(object)
# Even though I refused to do this for the iterable =
protocol, it's probably OK here.
# Objects with a length attribute should not be changed by =
accessing an item.
object[0]
except IndexError:
# This indicates the indexing is OK, but the sequence length =
is zero.
pass
except:
return 0
return 1=20
BasicSequence =3D _basicSequence()
class _sequence:
# This is a sequence, but it doesn't seem useful to inherit
"""A full sequence implements the same methods as a tuple.
A sequence can always be constructed from a BasicSequence,
see SequenceWrapper below.
=20
"""
def __implementedby__(self, object):
try:
len(object)
# I'm hoping this is less expensive for long sequences.
0*object + 0*object
object[:0]
# Even though I refused to do this for the iterable =
protocol, it's probably OK here.
object[0]
except IndexError:
# This indicates the indexing is OK, but the sequence length =
is zero.
pass
except:
return 0
return 1
Sequence =3D _sequence()
# It's kind of obnoxious having both BasicSequence and SequenceBase. One =
or both should
# probably be renamed.
class SequenceBase:
"""Class to simplify derivation of objects that satisfy the full =
sequence protocol.
This class provides the same functionality as a tuple.
At a minimum, __getitem__ and __len__ must be overridden. =
Concatenation and repition
return tuples by default. If other behaviour is desired, override =
__add__ and __mul__
as well.
Subclasses may also want to override __getslice__ for efficiency.
=20
"""
# The following list is based on section 2.1.5 in the library =
reference
# x in s: Should be take care of by the sequence prototcol(?)
# x not in s: Ditto
# s + t:
def __add__(self, other):
return tuple(self) + tuple(other)
# s * n, n * s:
def __mul__(self, n):
return tuple(self)*n
__rmul__ =3D __mul__
# s[i]
def __getitem__(self, i):
raise NotImplementedError
# s[i:j]
def __getslice__(self, start, stop):
l =3D []
for i in range(start, stop):
l.append(self[i])
return tuple(l)
# len(s)
def __len__(self):
raise NotImplementedError
# min(s)
# max(s) These two work automagically for any iterable.
class SequenceWrapper(SequenceBase):
def __init__(self, wrapped):
self.wrapped =3D wrapped
def __getitem__(self, i):
return self.wrapped[i]
def __len__(self):
return len(self.wrapped)
# Still to come, mutable sequences.
# Very basic testing.
class _iterable:
def __getitem__(self, i):
if 0 <=3D i < 5:
return i
raise IndexError
anInterable =3D _iterable()
class _basicSequence(_iterable):
def __len__(self):
return 5
aBasicSequence =3D _basicSequence()
aSequence =3D SequenceWrapper(aBasicSequence)
for obj, isIter, isBasic, isSeq in [((),1,1,1), ([],1,1,1),
(anInterable, 1, 0, 0),
(aBasicSequence, 1, 1, 0),
(aSequence, 1, 1, 1)]:
assert Iterable.__implementedby__(obj) =3D=3D isIter
assert BasicSequence.__implementedby__(obj) =3D=3D isBasic
assert Sequence.__implementedby__(obj) =3D=3D isSeq
print obj, "(", isIter, isBasic, isSeq, ") passed"
------=_NextPart_000_0361_01C0ACA1.59A4F970--