是否存在与Scala选项或Either相当的Python?

时间:2014-04-10 15:30:21

标签: python scala functional-programming

我真的很喜欢在Scala中使用Option和Either monad。 Python中有这些东西的等价物吗?如果没有,那么在没有抛出异常的情况下,处理错误或“缺少价值”的pythonic方法是什么?

9 个答案:

答案 0 :(得分:18)

嗯,实际上,函数说“我此时未定义”的pythonic方法是引发异常。

>>> int("blarg")
Traceback (most recent call last):
  ...
ValueError: invalid literal for int() with base 10: 'blarg'

>>> dict(foo=5)['bar']
Traceback (most recent call last):
  ...
KeyError: 'bar'

>>> 1 / 0
Traceback (most recent call last):
  ...
ZeroDivisionError: integer division or modulo by zero

这是因为python没有(通常有用的)静态类型检查器。 python函数在编译时不能语法状态,它具有特定的codomain;没有办法强制调用者匹配函数返回类型中的所有情况。

如果您愿意,可以(非自然地)编写一个Maybe包装器:

class Maybe(object):
    def get_or_else(self, default):
        return self.vaue if isinstance(self, Just) else default

class Just(Maybe):
    def __init__(self, value):
        self.value = value

class Nothing(Maybe):
    pass

但我不会这样做,除非你试图将scala中的东西移植到python而不会改变太多

答案 1 :(得分:8)

#include <stdio.h> #include <cs50.h> #include <string.h> #include <ctype.h> #include <stdlib.h> #define alpha_length 26 char secret(char character, int key); int main(int argc, char *argv[]) { //check that there are only two strings if (argc != 2) { printf("Usage: ./vignere k\n"); return 1; } //check that argv1 is alphabetical char *code = argv[1]; int code_len = strlen(code); for (int t = 0; t < code_len; t++) { if (!isalpha((unsigned char)code[t])) { printf("Alphabetical only!\n"); return 1; } } //get string from user to encrypt printf("plaintext: "); char *plaintext = get_string(); int text_len = strlen(plaintext); //array created out of user inputted plain text char cipher[text_len + 1]; //j counts the number of alphabetical characters so that it resets based on argv length int j = 0; //iterate over characters in array. If they are alpha then apply the function secret for (int i = 0; i < text_len; i++) { if (isalpha((unsigned char)plaintext[i])) { int index = j % code_len; int code_index = toupper((unsigned char)code[index]) - 'A'; cipher[i] = secret(plaintext[i], code_index); j = j + 1; } else { cipher[i] = plaintext[i]; } } cipher[text_len] = '\0'; printf("ciphertext: %s\n", cipher); return 0; } char secret (char character, int key) { int shift; // if the character is upper case then start with uppercase A and shift based on the appropriate character from argv1 if (isupper((unsigned char)character)) { shift = (int)character - 'A'; shift = shift + key; return 'A' + (shift % alpha_length); } else { // else start with lower case a shift = (int)character - 'a'; shift = shift + key; return 'a' + (shift % alpha_length); } } 在常规Python上添加了类型定义和类型检查(不是在运行时)。他们有mypyhttps://docs.python.org/3/library/typing.html#typing.Optional。更多https://www.python.org/dev/peps/pep-0484/#rationale-and-goals。 Intellij拥有插件支持,使其非常专业和流畅。

答案 2 :(得分:7)

在python中,由于缺少值,变量为None,所以你可以这样做。

vars = None

vars = myfunction()

if vars is None:
     print 'No value!'
else:
     print 'Value!'

甚至只是检查是否存在这样的值

if vars is not None:
     print vars

答案 3 :(得分:5)

我意识到派对的时间已经很晚了,但是在决定实施之前我来谷歌这个页面,所以也许我可以帮助其他人用谷歌搜索。我实现了它,你可以从pypi获得它pyther-maybe,它实现了Either和Maybe with Maybe作为Either的特殊子类。这个例子应该解释它是如何工作的:

import sys
from pyther_maybe import *

def save_div ( x, y ):
    if y == 0:
        return nothing() # alias of Maybe()
    else:
        return value(x / y) # alias of Maybe(x / y)

float_test = save_div(1.0, 3.0)

assert isinstance(float_test, Maybe)

if float_test: #nothing tests as false:
    float = float_test() # calling the container with no arguments returns its value
else:
    sys.exit("something went wrong")

print float

# or if you want to encode a reason:

def save_div ( x, y ):
    if y == 0:
        return left("You can't divide by zero, silly") # alias of Either(left=...)
    else:
        return right(x / y) # alis of Either(...)

float_test = save_div(4.2, 0.0)

assert isinstance(float_test, Either)

def fake_exit ( string ):
    print "We would have exited with:"
    print string
    return "Whatever value"

if float_test:
    # these two are te same
    float = float_test()
    float = float_test.right()
else:
    fake_exit(float_test.left())

# or in a shorter and more pleasant format
# does the same as above
float = float_test.extract(fake_exit)

print float # prints "Whatever value"

# Also, these containers are mutable:

box = nothing()

try:
    print box() # raises exception
except RightEitherException:
    print "We caught an exception"

def change_box():
    box(4)

change_box()
print box() # 4

它有更多的功能,其中一些在实践中相当无用(例如它也是一个迭代器,并且有下标符号,如pyther_maybe.either(x)[pyther_maybe.Right] == x

答案 4 :(得分:4)

您可以使用键入包(Python 3.6.9)进行游戏。使用以下命令会使类型检查器感到高兴

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'))

结果

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

如果您想向Either添加单行行为,则可以尝试

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})'

然后右键输入

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))

结果

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

答案 5 :(得分:1)

一个列表的长度总是为零或一个,它实现了一些与可选/也许类型相同的目标。您不会从Python中获得静态类型的好处,但是如果编写代码尝试使用“也许”而不显式“包装”它,即使在愉快的道路上,您也可能会遇到运行时错误。

答案 6 :(得分:1)

尝试一下:

from monad import Monad

class Either(Monad):
  # pure :: a -> Either a
  @staticmethod
  def pure(value):
    return Right(value)

  # flat_map :: # Either a -> (a -> Either b) -> Either b
  def flat_map(self, f):
    if self.is_left:
      return self
    else:
      return f(self.value)

class Left(Either):
  def __init__(self, value):
    self.value = value
    self.is_left = True

class Right(Either):
  def __init__(self, value):
    self.value = value
self.is_left = False

答案 7 :(得分:1)

从本质上讲,Python具有可选的Literal Type,但它并不相同。另外,这是python 3的Either数据类型的表示。

https://gist.github.com/MatteoGuadrini/98e79a9ab2bd6ae5badc41df89cfe338

答案 8 :(得分:0)

pip install either

该库创建于 2012 年,其开发状态为:稳定,根据 https://pypi.org/project/either/