On 2018-07-22 08:10, Steven D'Aprano wrote: > Indeed. And I think we ought to think carefully about the benefits and > costs of all of those variants separately. > > To me, the ?? operator seems like a clear and obvious win. The other > variants are more complex and the benefit is not as obvious to me, so I > haven't decided where I stand on them.
I like the ?? operator too; I like the short circuiting behavior a lot, and the semantics are simple. I guess I'd use the other operators fairly often, too, mostly in quick-n-dirty scripts. The one place where I miss them is when browsing through dictionaries that I get by querying a remote server and deserializing the resulting JSON. I foten have situation where the value I'm interested in is e.g. either in response[0]["addresses"]["workplace"]["email"], or in response["records"][0]["contactInfo"]["emails"][0], and any of these subrecords may be missing. Rewriting these using the ?[…] and ?. operators, I guess I would write something like this: tmp = response?.get("records") try: tmp = tmp?[0] except IndexError: tmp = None tmp = tmp?.get("contactInfo")?.get("emails") try: tmp = tmp?[0] except IndexError: tmp = None Is there a shorter way to write these with the "?[…]" and "?." operators? I guess the difficulty is that I need to swallow index and key errors, not just the type errors that come from indexing into None. For cases like the one above, I usually use something like nget(response, ["records"], [0], ["contactInfo"], ["emails"], [0]), where nget is defined as shown below (in this use case, the lack of short-circuiting isn't an issue): def nget(obj, *fields, default=None): for field in fields: if obj is None: return default if isinstance(field, str): obj = getattr(obj, field, None) elif isinstance(field, list): try: obj = obj.__getitem__(field[0]) except (TypeError, KeyError, IndexError): obj = None return obj class Test(): def __init__(self): self.x = [{"y": 42, "z": ["aBc", "def"]}, [1]] a = Test() print(nget(a, "x", [0], ["z"], [0], [1])) # B print(nget(a, "x", [0], ["y"])) # 42 print(nget(a, "z", [0], ["y"], default="not found")) # not found print(nget(a, "z", [57], ["y"], default="not found")) # not found It would probably not be hard to wrap this into a special object, to be able to write something like wrap(response)["records"][0]["contactInfo"]["emails"][0].unwrap(). "wrap" would change its argument into a proxy returning a special indexable variant of None on key errors, and that dictionary would also call "wrap" on the results of __getitem__. Something like this: class wrap(): SENTINEL = object() def __init__(self, obj): self.obj = obj def unwrap(self): return self.obj def __getitem__(self, key): try: return wrap(self.obj.__getitem__(key)) except (TypeError, AttributeError, KeyError): return wrap(None) a = [{"y": 42, "z": ["aBc", "def"]}, [1]] print(wrap(a)[0]["z"][0][1].unwrap()) I think that's more or less what pymaybe does, in fact. Cheers, Clément. _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/