-
Notifications
You must be signed in to change notification settings - Fork 288
-
Cross posting from Stack Overflow:
How do I do a type hint for attribute that might not exist on the object in Python?
For example:
class X: __slots__ = ('attr',) attr: int # either int or doesn't exist - what do I put here?
So, concretely:
>>> x = X() >>> x.attr Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'X' object has no attribute 'attr' >>> x.attr = 1 >>> x.attr 1
Slots may not always be involved, but I can see wanting to do something similar when specifying a Protocol.
Beta Was this translation helpful? Give feedback.
All reactions
Replies: 6 comments 8 replies
-
Currently this is not supported. There was a discussion about potentially adding Missing[] special form, but it was declined.
Right now the most correct way is to have some attribute defined as such:
class MyClass: def __init__(self) -> None: self.attr: int | None = None def method(self) -> None: self.attr = 1 # or whatever
Beta Was this translation helpful? Give feedback.
All reactions
-
Well no, this isn't correct. attr not being present is indication to a walker looking for attributes to look further up in the tree, None is a valid value, so would stop that traversal and cannot be used.
Beta Was this translation helpful? Give feedback.
All reactions
-
I'm curious what your desired behavior would be for such an attribute? Always emit an error/warning when accessing it, but also infer the resulting type as int?
Beta Was this translation helpful? Give feedback.
All reactions
-
In an ideal world, it would be an indication that mypy should report an error when the attribute is accessed other than through getattr(obj, 'attr', None).
Beta Was this translation helpful? Give feedback.
All reactions
-
👍 2
-
Also, if someone uses getattr, it tells the type checker what the attribute's type would be
Beta Was this translation helpful? Give feedback.
All reactions
-
@ewjoachim - how so?
Beta Was this translation helpful? Give feedback.
All reactions
-
Ah I understand that you were comparing
class X: y: Potential[int]
to
class X: y: int # might not exist
and in that case, of course, a type checker would need to complain if we access x.y without getattr (... or not in a try: except AttributeError?)
I was comparing
class X: y: Potential[int]
to
class X: pass # also, there might be a y
and in that case, it wouldn't even know that, if present, y would be an int. But your comparison makes more sense.
Beta Was this translation helpful? Give feedback.
All reactions
-
I am running into this when trying to type the open search bulk load responses.
There's so many permutations of these objects that I really just wanna be able to type it as a single object type in the listwith certain attributes that may or may not exist. I can type this really easily in type script, but there seems to be zero ways to do it in Python.
Beta Was this translation helpful? Give feedback.
All reactions
-
@piyh - what does it look like in Typescript?
Beta Was this translation helpful? Give feedback.
All reactions
-
This is a problem that is often ignored while being blatantly obvious.
Let's assume:
class Address: ... class Person: name: str address: Address | None
Now, the only option to do a partial update of a person is TypedDict:
class PartialUpdatePerson(TypedDict): name: NotRequired[str] address: NotRequired[Address | None]
We need this.
Beta Was this translation helpful? Give feedback.
All reactions
-
I feel like the existing
NotRequired[TYPE]
would does exactly what we need, just right now limited to TypedDict.
So all we need now, is to allow it to be used in classes as well.
Beta Was this translation helpful? Give feedback.
All reactions
-
Following that line of thought, it might even make sense in regular code.
A variable only sometimes existing is a core part of python after all:
try: result: str = do_something_maybe_stupid() except Exception as e: pass # end def result: NotRequired[str] e: NotRequired[Exception]
Or
if foo == "bar": offset = 12 offset: NotRequired[int]
Beta Was this translation helpful? Give feedback.
All reactions
-
What behaviour would NotRequired on a variable trigger on a type checker ? Type checkers already have all the info to know that a variable is maybe undefined, so I don't think there's value in making it explicit, given that the only thing that makes sense to do on such a variable is warn if it's used.
The only 2 ways to use a variable that might be undefined (as far as I can tell) are:
- In a
try: except NameError:block, and I don't think it's either pythonic nor something that I'd want a type checker to have a special case for (the same way that if you do:you still get a type checker error even though this could be normal Python code. Type checkers tend to not like that you write faulty code (regarding typing) and rely on exception catching to come back to the normal path.a: dict[str, str] = ... try: a[2] except KeyError: ...
locals().get("my_variable")and I don't expect a type checker to try and be smart if I start doing this kind of stuff.
So my point is: I don't see the value in doing this for local variables.
That said, using the already existing NotRequired for objects too makes sense :)
Beta Was this translation helpful? Give feedback.
All reactions
-
Unfortunately, this is not a hypothetical. Here is an example of this in the wild, in a reasonably well-known package: https://github.com/perforce/p4python/blob/31ac0a69eb508dcf557c9a620f3a109f8fec5817/P4.py#L124
spec = P4.Spec() # Attribute names are restricted, so most assignments are disallowed; the following will throw # spec.foo = "foo" # This will throw AttributeError, per above # print(spec.comment) # But it can be assigned to, which then makes it available spec.comment = "make it so" print(spec.comment) # This now prints as expected
Beta Was this translation helpful? Give feedback.