Consider the following toy class:
class Something(object): def __init__(self, a, b): self.a = a self.b = b def __iter__(self): yield ('a', self.a) yield ('b', self.b) x = Something(1, 2) print(tuple(x)) # (('a', 1), ('b', 2)) print(dict(x)) # {'a': 1, 'b': 2} However, I would like it to behave like the following:
print(tuple(x)) # (1, 2) print(dict(x)) # {'a': 1, 'b': 2} How could I do that?
EDIT
I am well aware that this could be achieved explicitly, e.g. (following dict() naming conventions):
class Something(object): def __init__(self, a, b): self.a = a self.b = b def items(self): yield ('a', self.a) yield ('b', self.b) def values(self): yield self.a yield self.b BUT clearly some objects do behave differently when casted to dict() or tuple() respectively. For example, the behavior of dict() itself (but also, for example, collections.OrderedDict and other mappings in the collections module) do something similar (uses keys while I would like to get values) just fine:
import collections dd = collections.OrderedDict((('a', 1), ('b', 2))) print(dict(dd)) {'a': 1, 'b': 2} print(tuple(dd)) ('a', 'b') print([x for x in dd]) ['a', 'b'] EDIT 2:
Another way of seeing this is that when something goes through dict() EITHER it behaves differently depending on the type of __iter__ OR looks like sometimes it relies on __iter__ and sometimes it relies on something else. The question is what is that something else (or what kind of type checks happen at this level), how to get access to this alternate behavior and eventually discussing potential limitations.
I could well be that at the end of the day a custom class Something behaving as I described cannot be crafted in Python, because e.g. __iter__ must return the keys of the mapping.
dicttype iterates only one way (by keys), and provides different methods to iterate over key-value pairs, or just values.dict()ortuple()on them e.g.dd = {'a': 1, 'b': 2}anddict(dd) == {'a': 1, 'b': 2}whiletuple(dd) == ('a', 'b')(orimport collections; dd = collections.OrderedDict((('a', 1), ('b', 2)))) just to avoid the likely shortcut of do-nothing behavior fordict(dict).dictandtuple). You can't change those, you can only change the type, and the type has no concept of "When I'm being converted to a tuple, do this." You can define how the object is iterated over, but not how the built-in uses that data.('a', 1) ('b', 2)and you'll get what you see, or you can decide that you're iterating to1 2and you'll get the tuple result you want, but not both. And importantly: even if you could do both, you shouldn't because it makes your type behave badly. Callers can't expect it to be consistent, which is Bad.