> That's a very superficial similarity: a list ['a', 'b', 'x', 'y'] is > something like a mapping {0: 'a', 1: 'b', 2: 'x', 3: 'y'}. Seems logical, > since in both cases we write collection[2] and get 'x' back. > > But think about it a bit more, and you will see that the behaviour is in > fact *very different*. For example: > > the_list = ['a', 'b', 'x', 'y'] > # the_list is equivalent to {0: 'a', 1: 'b', 2: 'x', 3: 'y'} > the_list.insert(0, 'z') > # the_list is now equivalent to {0: 'z', 1: 'a', 2: 'b', 3: 'x', 4: 'y'} > > Every existing "key:value" pair has changed! What sort of mapping operates > like that?
It's like you said: "Animals with four legs can usually run, eat etc. But birds can fly! What sort of animal flies?" Well. birds. You can easily extend dict and create a new class that imitate list, tuple or str without any problem. Sequences are an extension of maps. > Have you ever actually wanted to use sequences or maps indifferently? To do > what? Not my code, but this is an example: https://github.com/thieman/dagobah/blob/master/dagobah/daemon/daemon.py def replace_nones(dict_or_list), line 27 On 23 March 2016 at 12:26, Steven D'Aprano <st...@pearwood.info> wrote: > On Wed, 23 Mar 2016 06:54 am, Marco S. wrote: > > > I noticed that the sequence types does not have these methods that the > map > > types has: get(), items(), keys(), values(). > > > > It could seem useless to have them for sequences, > > That's putting it mildly. > > > but I think it will ease > > the creation of functions and methods that allow you to input a generic > > iterable as parameter, but needs to use one of these methods in case the > > parameter is a map. > > Perhaps it would. But countering that is the disadvantage that you're > adding > methods to sequences that have no sensible meaning for a sequence. > > Strings and floats are quite different things, but occasionally it makes > sense to write a function that accepts either a string or a float. Perhaps > we could introduce methods to strings and floats to blur the difference, > to "ease the creation of functions and methods that allow you to input a > generic scalar (string, float, int) as parameter..."? > > Some of these methods would be easy: > > class float: > def upper(self): > return self > > > Some a bit more work: > > def find(self, substring, start, end): > s = str(self) > return s.find(substring, start, end) > > but some perplex me. What would (27.25).expand_tabs() do? > > > Of course this is silly. Even Perl and Javascript don't go this far in > making floats and strings interchangeable. This is a good thing: if you're > calling some_float.upper(), you've almost certainly made a programming > error, and you don't want this nonsensical method call to silently succeed. > > So it is with sequences and mappings. They are *not* the same sort of > thing, > even though they have a few superficial similarities, and they shouldn't > try to behave as the same thing. > > What should some_dict.append(None) do? The very concept is nonsense: dicts > aren't *ordered sequences*, you can't append to a dict. Maybe you could > give dicts a method *called* "append", but it wouldn't mean the same thing > as list.append, and it probably wouldn't have the same signature: > > > list.append(value) > Appends value to the end of the list. > > dict.append(key, value) > Adds a new key to the dict, with the given value. > Same as dict[key] = value. > > > Forcing these two completely different methods to have the same name > doesn't > do anything useful. That's like putting a door handle on your iPhone, in > case some day you want to treat your iPhone as a door. > > > > In one word, it will facilitate duck typing. > > I think you have misunderstood the purpose and meaning of duck-typing. > > Duck-typing is not about forcing unrelated, dissimilar types to have the > same interface just in case you want to write a function that will accept > either type. > > Duck-typing is about accepting anything which "quacks like a duck". If all > you need is something which lays an egg, you shouldn't care whether it is a > chicken or a goose or a duck. It doesn't mean that dogs and cats should > have a "lay_egg" method, just in case somebody wants to accept a duck or a > dog. > > > > For the same reason, I would suggest the introduction of a new map type, > > vdict, a dict that by default iterates over values instead over keys. So > a > > vdict object "d" wiil have iter(d) == iter(d.values()), and should also > > have a count() method, like sequence types. > > I don't really see the point in this. What sort of function will expect to > iterate over a mapping, but not care whether it is getting keys or values? > > Sure, there are generic functions that will iterate over *any iterable*, > and > you can pass dict.keys() or dict.values(), and both will work fine. That's > exactly what duck-typing is about. > > But let's imagine this hypothetical function that will accept any mapping, > and some mappings will iterate over keys and some mappings iterate over > values. What would you do with it? > > def walk(the_dict): > for obj in the_dict: > print("Key =", obj) # That's wrong, it might be a value. > value = the_dict[obj] # That's wrong too. > the_dict[obj] = "processed" # Still wrong. > > > There's nothing useful or interesting you can do with a mapping and > something which might be a key, or might be a value, but you don't know > which. You can treat them in isolation, as if they had nothing to do with a > dict, and that's about it. But in that case, why insist on a dict? > > walk(the_dict.keys()) > walk(the_dict.values()) > walk(the_list) > walk(the_iterator) > > > > Indeed sequences are, in my humble opinion, a specialized case of maps, > > when keys are numeric only, are always contiguous without gaps and start > > from 0. > > That's a very superficial similarity: a list ['a', 'b', 'x', 'y'] is > something like a mapping {0: 'a', 1: 'b', 2: 'x', 3: 'y'}. Seems logical, > since in both cases we write collection[2] and get 'x' back. > > But think about it a bit more, and you will see that the behaviour is in > fact *very different*. For example: > > the_list = ['a', 'b', 'x', 'y'] > # the_list is equivalent to {0: 'a', 1: 'b', 2: 'x', 3: 'y'} > the_list.insert(0, 'z') > # the_list is now equivalent to {0: 'z', 1: 'a', 2: 'b', 3: 'x', 4: 'y'} > > Every existing "key:value" pair has changed! What sort of mapping operates > like that? > > del the_list[2] > # the_list is now equivalent to {0: 'z', 1: 'a', 2: 'x', 3: 'y'} > > I've said to delete the "key" 2, but there it still is. "Key" 2 still > exists, it just has a different value! And it is "key" 4 which has been > deleted! But not the *value* attached to "key" 4, that's been moved to 3! > > What sort of crazy mapping behaves like this? > > The answer is, of course, *no* sort of mapping. Sequences are not mappings. > The only reason we think of them as kinda-sorta like mappings is because of > a superficial similarity between key:value and index:item. That similarity > is real, but virtually everything else about mappings and sequences is > different. > > > > > This way we will have a simpler way to let people to use sequences > > or maps indifferently, and let the code untouched. > > Have you ever actually wanted to use sequences or maps indifferently? To do > what? > > The only case I've ever seen of that is the dict constructor, and > dict.update, which will accept either a mapping or a sequence of (key, > value) pairs. > > > > > -- > Steven > > -- > https://mail.python.org/mailman/listinfo/python-list > -- https://mail.python.org/mailman/listinfo/python-list