Is there a Python equivalent for Scala's Option or Either?

前端 未结 8 1595
忘掉有多难
忘掉有多难 2021-02-05 01:26

I really enjoy using the Option and Either monads in Scala. Are there any equivalent for these things in Python? If there aren\'t, then what is the pythonic way of handling erro

8条回答
  •  遥遥无期
    2021-02-05 01:34

    You can play with typing package (Python 3.6.9). Using following makes type checker happy

    from typing import Optional, Union
    
    
    def parse_int(s: str) -> Optional[int]:
        try:
            return int(s)
        except:
            return None
    
    
    print('-- optional --')
    print(parse_int('123'))
    print(parse_int('a'))
    
    
    def parse_int2(s: str) -> Union[str, int]:
        try:
            return int(s)
        except Exception as e:
            return f'Error during parsing "{s}": {e}'
    
    
    print('-- either --')
    print(parse_int2('123'))
    print(parse_int2('a'))
    

    Result

    -- optional --
    123
    None
    -- either --
    123
    Error during parsing "a": invalid literal for int() with base 10: 'a'
    

    If you want to add monadic behaviour to Either you can try this

    from typing import TypeVar, Generic, Callable
    
    A = TypeVar('A')
    B = TypeVar('B')
    C = TypeVar('C')
    
    Either = NewType('Either', Union['Left[A]', 'Right[C]'])
    
    
    class Left(Generic[A]):
        def __init__(self, value: A):
            self.__value = value
    
        def get(self) -> A:
            raise Exception('it is left')
    
        def get_left(self) -> A:
            return self.__value
    
        def flat_map(self, f: Callable[[B], Either]) -> Either:
            return self
    
        def map(self, f: Callable[[B], C]) -> Either:
            return self
    
        def __str__(self):
            return f'Left({self.__value})'
    

    and right type

    class Right(Generic[B]):
        def __init__(self, value: B):
            self.__value = value
    
        def flat_map(self, f: Callable[[B], Either]) -> Either:
            return f(self.__value)
    
        def map(self, f: Callable[[B], C]) -> Either:
            return Right(f(self.__value))
    
        def __str__(self):
            return f'Right({self.__value})'
    
    
    def parse_int(s: str) -> Union[Left[str], Right[int]]:
        try:
            return Right(int(s))
        except Exception as e:
            return Left(f'Error during parsing {s}: {e}')
    
    def divide(x: int) -> Union[Left[str], Right[int]]:
        return Right(4 / x) if x != 0 else Left('zero !!!')
    
    print(parse_int('1').map(lambda x: x * 2))
    print(parse_int('a').map(lambda x: x * 2))
    print(parse_int('2').flat_map(divide))
    print(parse_int('0').flat_map(divide))
    

    Result

    Right(2)
    Left(Error during parsing a: invalid literal for int() with base 10: 'a')
    Right(2.0)
    Left(zero !!!)
    

提交回复
热议问题