T
Tom Anderson
hi all,
i've got a few proposals to do, in one way or another, with the map
builtin. i'd like to hear what people think.
firstly, collections, and things that look like collections, should
support the call operator; the implementation should look like:
def __call__(self, arg):
return self[arg]
then, you could use collections as functions. after all, collections and
functions are just two different ways of representing mappings (dicts are
general mappings, and lists are mappings of the natural numbers which have
a contiguous domain), so it makes sense for them to be somewhat
interchangeable. specifically, this would make it possible to use dicts
and lists as first arguments to map, eg:
details = {
"surnname": "Cleese",
"firstname": "John",
"dob": 19391027,
"birthplace": "Weston-super-Mare",
"height: 1.95,
"favourite work": "the documentary about lemurs"
}
template = ("firstname", "surname", "height")
info = map(details, template)
okay, so maybe you're not convinced, but this is something i find myself
wanting to do all the time, so i either end up throwing lambdas all over
the place to glue the dicts into map, or writing a little helper function
like so:
def functor(dict):
return lambda arg: dict[arg]
this isn't hard, of course, but it's the sort of thing that ought to be
done once and only once.
there's a case to be made that function objects should also have
__getitem__, so they look like mappings, but that's harder, since they
can't really supply a keys method, so they can't be full mappings.
however, i do wonder if sequences could have a keys method, which would
look like:
def keys(self):
return range(len(self))
so they look like mappings of a range of the natural numbers. the problem
with that is that the sequence and mapping versions of __iter__ are
incompatible. oh well.
secondly, map should work on dicts as well as sequences. map has semantics
that look like this:
map(fn, x) = fn(x)
at the moment, x has to be a sequence; i'd like to allow x to be a
mapping. the implementation is a doddle:
def mapdict(fn, dict):
mdict = []
for key in dict:
mdict[key] = fn(dict[key])
return mdict
again, trivial, but if i'm writing this out in almost every program i
write, other people must be too, so it's worth putting in the core.
now, that function is a version of map for dicts; i'd like to see one
function which supports sequences and mappings. i'm not entirely sure how
you'd decide whether an input was a sequence or a mapping, though; the
best i've come up with is using hasattr(x, "keys") to see if it looks like
a mapping (which falls down if we give sequences a keys method!).
thirdly, i'd like map to try to preserve the type of the sequence; if you
feed it a tuple, you get a tuple back, and if you feed it a string, you
get a string back. this isn't hard to do, but is hard to do well. the
simple way forward is to pull out the type of the parameter and try to use
it to construct a return value from a list:
def preservingmap(fn, seq):
mseq = map(fn, seq)
try:
mseq = type(seq)(mseq)
except TypeError:
pass
return mseq
however, this requires the constructor of sequence-like types to
essentially be a copy constructor; this may be too great a restriction on
programmers.
also, we'd need to redefine the implementation of __str__ for sequences;
at present, it's the same as __repr__, but that doesn't work in the above
scheme, since it's far from being a copy constructor. in particular, we
want it to be such that, for a string s, str(list(s)) == s. i think it has
to look like:
def __str__(self):
return reduce(lambda a, b: a + b, map(str, self))
so that it just returns the concatenation of the stringification of its
elements. this isn't much use for the general task of displaying lists to
humans, but that's what repr is for. str is about converting an object to
a string, and this is surely the most natural way for sequences.
anyway, that's it. am i bonkers, or does any of that sound like a good
idea?
tom
i've got a few proposals to do, in one way or another, with the map
builtin. i'd like to hear what people think.
firstly, collections, and things that look like collections, should
support the call operator; the implementation should look like:
def __call__(self, arg):
return self[arg]
then, you could use collections as functions. after all, collections and
functions are just two different ways of representing mappings (dicts are
general mappings, and lists are mappings of the natural numbers which have
a contiguous domain), so it makes sense for them to be somewhat
interchangeable. specifically, this would make it possible to use dicts
and lists as first arguments to map, eg:
details = {
"surnname": "Cleese",
"firstname": "John",
"dob": 19391027,
"birthplace": "Weston-super-Mare",
"height: 1.95,
"favourite work": "the documentary about lemurs"
}
template = ("firstname", "surname", "height")
info = map(details, template)
okay, so maybe you're not convinced, but this is something i find myself
wanting to do all the time, so i either end up throwing lambdas all over
the place to glue the dicts into map, or writing a little helper function
like so:
def functor(dict):
return lambda arg: dict[arg]
this isn't hard, of course, but it's the sort of thing that ought to be
done once and only once.
there's a case to be made that function objects should also have
__getitem__, so they look like mappings, but that's harder, since they
can't really supply a keys method, so they can't be full mappings.
however, i do wonder if sequences could have a keys method, which would
look like:
def keys(self):
return range(len(self))
so they look like mappings of a range of the natural numbers. the problem
with that is that the sequence and mapping versions of __iter__ are
incompatible. oh well.
secondly, map should work on dicts as well as sequences. map has semantics
that look like this:
map(fn, x) = fn(x)
at the moment, x has to be a sequence; i'd like to allow x to be a
mapping. the implementation is a doddle:
def mapdict(fn, dict):
mdict = []
for key in dict:
mdict[key] = fn(dict[key])
return mdict
again, trivial, but if i'm writing this out in almost every program i
write, other people must be too, so it's worth putting in the core.
now, that function is a version of map for dicts; i'd like to see one
function which supports sequences and mappings. i'm not entirely sure how
you'd decide whether an input was a sequence or a mapping, though; the
best i've come up with is using hasattr(x, "keys") to see if it looks like
a mapping (which falls down if we give sequences a keys method!).
thirdly, i'd like map to try to preserve the type of the sequence; if you
feed it a tuple, you get a tuple back, and if you feed it a string, you
get a string back. this isn't hard to do, but is hard to do well. the
simple way forward is to pull out the type of the parameter and try to use
it to construct a return value from a list:
def preservingmap(fn, seq):
mseq = map(fn, seq)
try:
mseq = type(seq)(mseq)
except TypeError:
pass
return mseq
however, this requires the constructor of sequence-like types to
essentially be a copy constructor; this may be too great a restriction on
programmers.
also, we'd need to redefine the implementation of __str__ for sequences;
at present, it's the same as __repr__, but that doesn't work in the above
scheme, since it's far from being a copy constructor. in particular, we
want it to be such that, for a string s, str(list(s)) == s. i think it has
to look like:
def __str__(self):
return reduce(lambda a, b: a + b, map(str, self))
so that it just returns the concatenation of the stringification of its
elements. this isn't much use for the general task of displaying lists to
humans, but that's what repr is for. str is about converting an object to
a string, and this is surely the most natural way for sequences.
anyway, that's it. am i bonkers, or does any of that sound like a good
idea?
tom