It wouldn’t be, but I think your use of the term “valid” is a bit misleading. No one is saying this is to work around invalid code. The syntax proposal is to deal with data structures where an attribute is defined to be optional. So in your hypothetical, the type checkers would require that you use getattr() or hasattr() as appropriate to avoid raising an AttributeError. With the proposed syntax the type checkers would still do the same checks, but the suggestion is the overall written code would be more succinct and easier to follow. So both scenarios lead to the same outcome, just with different approaches to the coding.
For me, it’s more about making scripting easier than correctness of code as the proposed syntax doesn’t enable some new coding abilities that Python didn’t have before. And for long-lasting production code, being more verbose in this specific instance isn’t the end of the world.
Good example (even if NotRequired is only for TypedDicts, but it’s not important). Now I catch there could be attributes defined conditionally, so they can be present or not. I tried your code in my IDE (PyCharm) and actually its static type checker does not emit a warning if I try to access bar. So with this kind of classes, the Guido’s proposal is useful.
My question is: is this pattern commonly used? Guido posted a real world example in TypeScript, but not in Python.
Well, yes, you’re right, it’s only a syntactic sugar, but it’s quite handy. NULL checks are frequent when you code.
I was able to track down the source of several dozen ? in a web application. It was used to protect against a single possible missing HTML element. While that’s quite rare, if the element was missing, the entire call tree would execute, short-circuiting at every ?.
The use of optional fields is not rare in Python. This could lead to entire call trees short-circuiting on ? quite often, unless proper guards were implemented.
The postfix notation seems quite confusing to me (especially when switching between JS and Python).
I think event?["timeRange"]?["startTime"] is good.
And yes, it has to convert AttributeErrors and KeyErrors to None for it to be fully useful.
I’m also eagerly waiting for this feature, it’s tiresome having to put guards around most key accesses from a json api.
What I can tell about the opinions of people regarding this PEP from what I read :
? is a meaningful character for “safe” operations.
Let refer ??, ?., ?[...] as “safe or”, “safe access”, “safe indexation”, these would be useful but are cryptic, and quantitative usage of them would decrease readability of the code.
One possibility could be to introduce a “family of safe operators” with this kind of explicit names : ?or, ?dot, ?at, …
Also, from my usage cases, I use None checks mainly within class constructors.
One usage I frequently require is about mutually exclusive arguments for contructors, so maybe some ?mutex (following the “family of safe operators” syntax just above) function can be pertinent :
class Person:
def __init__(self, name, age=None, birth_year=None):
self.name = name
match ?mutex [age, birth_year]: # asserting exactly one list element is not None
case 0 : self.age = age
case 1 : self.age = current_year - birth_year
I’m a bit late to the party, but I haven’t seen the following proposed:
The things that I seemingly type over and over again are “if a is None:”/ “if a is not None:” and “a if a is not None else b”. The latter is covered by the proposed ?? operator, but I agree with others that it feels a bit un-pythonic. A keyword like otherwise would be better, imo.
As for the former, why not let a? mean a is not None? Then you could write
if reader?:
reader.feed_data(data)
for example.
Or (example from the PEP)
mangle_from_ = policy.mangle_from_ if policy? else True
meaning
mangle_from_ = True if policy is None else policy.mangle_from_
I think this is very easy to teach and a real quality-of-life improvement.
I’d also advocate writing the type Optional[int] as int? in type annotations. You can think of it as pattern matching: if you have a variable a of type int?, then checking with if a?: means we can narrow a to the type int within the if.
Well, sometype | None is quite short. I’m not saying I don’t like sometype?. On the contrary, I find it useful and simple. But are you sure we want yet another way to express Optional[sometype]?
An idea (not the solution, something i hope inspiring, a bit mind-twisting though)
Think about the “neutral elements” in mathematics (you add 0, or multiply by 1, you change nothing. Also you multiply by zero you get zero… I am not going to talk about associativity, commutativity, etc… right now).
I can say the requirement here is to have access to methods able to manage the “Noneness” of different objects in a concise way, but the difficulty is to make it not cryptic. Thus the idea here (I am trying to say it correctly) is to “infer” non-trivial “Noneness” properties into a “neutral” element.
→ Basically I’ve tried to encase Noneness properties management into operations involving one “neutral-safe” element.
Probably the code below will be clearer than any further explanation (elmt stands for ‘element’) :
# -*- coding: utf-8 -*-
class SafeElement:
def __init__(self, elmt=None):
self._elmt = elmt
def __call__(self, elmt=None):
if elmt is None:
return self._elmt
else:
return SafeElement(elmt)
def __bool__(self):
return False if self._elmt is None else True
def __or__(self, elmt):
return self if self else SafeElement(elmt)
__add__ = __or__
def __and__(self, elmt):
elmt = SafeElement(elmt)
return SafeElement() if not self or not elmt else SafeElement(True)
__mul__ = __and__
def _mutex(self, elmt):
if self and (elmt is not None):
raise MutexError(self(), elmt)
else:
return self + elmt
__mod__ = _mutex
# __pow__ = _mutex # not used because __pow__ is right-associative
def __rpow__(self, other):
return self._mutex(other)
def __at__(self, idx):
if type(self()) is list:
if type(idx) is int:
if idx < len(self()):
return SafeElement(self()[idx])
elif type(self()) is dict:
if type(idx) is str:
if idx in self().keys():
return SafeElement(self()[idx])
return SafeElement()
__matmul__ = __at__
def __repr__(self):
return "SafeElement : " + self().__repr__()
class MutexError(Exception):pass
# =============================================================================
# The "neutral-safe" element
# =============================================================================
Ω = SafeElement()
# =============================================================================
# Usage
# =============================================================================
# Basic checks for None
bool(Ω) # False
bool(Ω(1)) # True
bool(Ω(0)) # True
bool(Ω(None)) # False
# Something tricky
Ω # SafeElement : None
Ω() # None
Ω(1) # SafeElement : 1
Ω(1)() # 1
# Safe "OR" operator
(Ω + 1 + 2) # SafeElement : 1
(Ω + None + 2) # SafeElement : 2
(Ω + 1 + 2)() # 1
(Ω + None + 2)() # 2
# Safe indexation
oak = {'tree':{'branch':{'leaf':'chlorophyll'}}}
(Ω(oak)@'tree'@'branch'@'leaf')() # 'chlorophyll'
(Ω(oak)@'tree'@'branch'@'door'@'corridor')() # None
# Safe mutex
Ω % None % 2 # SafeElement : 2
Ω % None % 2 % None % None # SafeElement : 2
(Ω % None % 2 % None % None)() # 2
Ω % 1 % 'This raises a MutexError' # raise MutexError
I chose the Ω unicode character to instanciate one “neutral-safe” element. Once the element is created, several operations can propagate the Noneness properties and eventually you obtain the ‘safe access’, ‘safe or’, ‘safe indexing’, and ‘safe mutex’ operations, just by introducing one element with propagating properties.
One main drawback of the implementation here above is to require to __call__ the SafeElement instance to get the element back.
It makes Python more hieroglyphic like, something I dislike in other languages. I like the joke that you turn your pseudo code into a running program by changing the file type from txt to py!
I’ve also posted this on the original thread because I can’t decide which one is current - sorry for double post.
I’m a -1 for this for three reasons:
It makes Python more hieroglyphic like, something I dislike in other languages. I like the joke that you turn your pseudo code into a running program by changing the file type from txt to py!
If I’m reading this correctly, all of these are variations on ONE alternative solution, which is “validate everything first”. This is not a solution in all situations, and it’s not really a fair representation to give six links to variations of the same thing.
I have yet to see any good solution for the variation where you don’t want to predefine every single detail of the entire JSON tree - which is an extremely common use-case for me. I have used these kinds of operators frequently in multiple other languages.
It definitely belongs on the other thread. This one’s a meta conversation about how to break the original conversation out of its loop of new people reposting old points without realising because they didn’t want to read the 100s of already existing posts (which, I want to say in the gentlest/most unaccusatory way, does include your post I’m afraid ).
I haven’t added to the original discussion because it’s long enough already, and someone else has already made this point.
Trying to stick to the topic of the OP: I can’t see a good way out of this circle. You can try to count hearts, but placing hearts in the middle of a 150-post thread feels pointless. You can try to use polls, but when I tried that in a thread of my own it didn’t particularly clarify the discussion. And you can reply to me and explain that function defaults aren’t the only use-case for ??=, but I’m sure someone in the other thread already did, so that just perpetuates the circle.