Python typing str or none

Check if a field is typing.Optional

Where fields is from dataclasses , I wanna list all the Optional fields. What I’m doing at this moment to solve it, is using a Regex-based on the field name, but I don’t like this approach. Is there a better way of doing it?

6 Answers 6

For reference, Python 3.8 (first released October 2019) added get_origin and get_args functions to the typing module.

assert get_origin(Dict[str, int]) is dict assert get_args(Dict[int, str]) == (int, str) assert get_origin(Union[int, str]) is Union assert get_args(Union[int, str]) == (int, str) 
def is_optional(field): return typing.get_origin(field) is Union and \ type(None) in typing.get_args(field) 

For older Pythons, here is some compatibility code:

# Python >= 3.8 try: from typing import Literal, get_args, get_origin # Compatibility except ImportError: get_args = lambda t: getattr(t, '__args__', ()) \ if t is not Generic else Generic get_origin = lambda t: getattr(t, '__origin__', None) 

Note: typing.Optional[x] is an alias for typing.Union[x, None]

Now, one could inspect the attributes of your input field annotation to check if it is defined like Union[x, None]:
You can read its attributes __module__ , __args__ and __origin__ :

from typing import * def print_meta_info(x): print(x.__module__, x.__args__, x.__origin__) x = Optional[int] print_meta_info(x) # 'typing', (class Int,), typing.Union x = Union[int, float] print_meta_info(x) # 'typing', (class int, class float), typing.Union x = Iterable[str] print_meta_info(x) # 'typing', (class int,), typing.Iterable 

You need to take this steps to define your checker:

  1. Make sure that the annotation has the keys __module__ , __args__ and __origin__
  2. __module__ must be set to ‘typing’. If not, the annotation is not an object defined by the typing module
  3. __origin__ value is equal to typing.Union
  4. __args__ must be a tuple with 2 items where the second one is the class NoneType ( type(None) )

If all conditions are evaluated to true, you have typing.Optional[x]

You may also need to know what is the optional class in the annotation:

x = Optional[int].__args__[0] print(x) # class int 

Another approach (That works on both python 3.7 & and 3.8) is to relay on how the set Union operation works:

The logic is that Optional type can’t be Optional er. While you can’t directly know if a type is nullable/optional, Optional[type] would be the same as type is the type is optional and other ( Union[type,None] to be exact) otherwise.

Union[SomeType,None] == Union[Union[SomeType,None]] 

(the first is eqivalent to Optional[SomeType] and the second to Optional[Optional[SomeType]]

Читайте также:  Coding python on linux

This allows very easy check for Optional values:

from dataclasses import dataclass, fields from typing import Optional @dataclass() class DC: x: Optional[str] = None y: str = "s" def get_optional_fields(cls): fields_list = fields(cls) return [ field.name for field in fields_list if field.type == Optional[field.type] ] if __name__ == '__main__': print(get_optional_fields(DC())) # ['x'] 

Optional[X] is equivalent to Union[X, None] . So you could do,

import re from typing import Optional from dataclasses import dataclass, fields @dataclass(frozen=True) class TestClass: required_field_1: str required_field_2: int optional_field: Optional[str] def get_optional_fields(klass): class_fields = fields(klass) for field in class_fields: if ( hasattr(field.type, "__args__") and len(field.type.__args__) == 2 and field.type.__args__[-1] is type(None) ): # Check if exactly two arguments exists and one of them are None type yield field.name print(list(get_optional_fields(TestClass))) 

Python 3.10 adds the notation T | None to specify a union type (see PEP 604). In my local Python 3.10.7, types.get_origin converts this to types.UnionType (not `typing.Union). So the following works for flat unions (but see SimSimY’s answer regarding nested unions).

import typing import types def field_is_optional(cls: type, field_name: str): """A field is optional when it has Union type with a NoneType alternative. Note that Optional[] is a special form which is converted to a Union with a NoneType option """ field_type = typing.get_type_hints(cls).get(field_name, None) origin = typing.get_origin(field_type) #print(field_name, ":", field_type, origin) if origin is typing.Union: return type(None) in typing.get_args(field_type) if origin is types.UnionType: return type(None) in typing.get_args(field_type) return False 
from dataclasses import dataclass from typing import Optional, Union @dataclass class A: foo : Optional[int] = None bar : int|None = None baz : Union[int, float, None] = None x : int = 1 a=A() assert field_is_optional(type(a), "foo") assert field_is_optional(type(a), "bar") assert field_is_optional(type(a), "baz") assert field_is_optional(type(a), "x") == False 

Источник

Python 3 type hinting for None?

Python 3 doesn’t have type checking. It has pep 3107 annotations, that some external tool might consider type hints. The pep specifically doesn’t assign any semantics whatsoever to the annotations.

5 Answers 5

def foo( hello: str='world', bar: str=None, another_string_or_None: str|. =None): . 

I’ve noticed that your use case is «something or None».

Читайте также:  Функция произвольного числа аргументов питон

Since version 3.5, Python supports type annotations via typing module. And in your case, the recommended way of annotating is by using typing.Optional[something] hint. This has exact meaning you’re looking for.

Therefore the hint for another_string_or_None would be:

import typing def foo( hello: str='world', bar: str=None, another_string_or_None: typing.Optional[str]=None): . 

This is what I was looking for to do optional type semantics. Alternatively, to do variant type semantics, typing.Union exists, or if you care about mapping of multiple signature to return types, @typing.overload exists too

@solstice333 Yeah, as a sidenote to my answer: typing.Union[None, ABC] would work the same as typing.Optional[ABC]. And if you have typing.Optional[typing.Union[int, str]], you can change it to typing.Union[None, int, str].

Python 3.10 will support your original desired notation: str | None .

>>> def nothing(nun: None) -> None: . return nun . >>> nothing(None) >>> 

Since these annotations are meaningless to Python beyond being in/correct syntax, it’s sort of up to the tools.

If you use typecheck-decorator for example, then you’ll need to use type(None) :

>>> import typecheck as tc >>> >>> @tc.typecheck >>> def nothing(nun: type(None)) -> type(None): . return nun . >>> nothing(None) >>> nothing(0) typecheck.framework.InputParameterError: nothing() has got an incompatible value for nun: 0 >>> nothing(False) typecheck.framework.InputParameterError: nothing() has got an incompatible value for nun: False 

Typecheck also allows you to somewhat more clearly «add more than one type hint with» with tc.any() (OR), tc.all() (AND), and far more besides.

Beware that tc.none() is a NAND-like predicate; not what you are looking for — with no arguments it will accept any type, equivalent to tc.all() or the more apt tc.anything .

Источник

Python Type Annotations, Returns Type Or None For Error

What is the Pythonic way to type annotate a function to indicate «returns type xxxx or None for an error?» I often have functions which return data of a single type — or None to indicate an error. e.g.

def GoodName(name): if name = '': return None if name = 'Kevin': return 'good' else: return 'meh' 
def GoodName(name:str) -> Optional[str]: if name = '': return None if name = 'Kevin': return 'good' else: return 'meh' 
  • Is there a succinct way to say that a function returns a type except in cases of error, which does not ripple through later code?
  • Would it be more Pythonic to return something like a null string to indicate errors, so as not to ‘contaminate’ downstream typing? (or a bad integer if the return type is integer)
  • Or is the Pythonic way to throw an error and require the caller to catch it?
Читайте также:  Label example in java

If the function IS supposed to return a string then it should return a string, not None. Optional lets the user know that this function might not return a string and they should handle the case when it does not

What do you mean by it «contaminates» the type? If it can return None for errors, and you want to indicate that via explicit typing, that’s exactly what you want. You want Optional[str] to be a separate type from str that needs to be dealt with. Haskell’s Maybe type enforces dealing with failure at compile time, and that’s a good thing.

If the function always returns string, then I can pass its result to a later function which expects a string, without the type checker complaining. But even if I include logic to ensure the call to the later function never happens with None, the type checker complains that the later function gets called with a string-or-None. That’s what I’m calling «contaminated.»

I see an example of the return-special-value-for-error in the case of string.find(), and the throw-error in the case of string.index(). The value of find’s approach is that it makes type annotations simpler. The value of index’s approach is that it ensures later code does not forget to deal with the exception. Is one of these approaches more common in Python coding?

» But even if I include logic to ensure the call to the later function never happens with None, the type checker complains that the later function gets called with a string-or-None. » You should give an example of that, because if you are correctly handling the optional None, then the type checker shouldn’t complain. They pythonic thing would be to raise an exception, usually. Although, what you are describing is a common pattern (one that I try to avoid).

Источник

Оцените статью