我真的很喜欢在Scala中使用Option和Either monad。 Python中有这些东西的等价物吗?如果没有,那么在没有抛出异常的情况下,处理错误或“缺少价值”的pythonic方法是什么?
答案 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上添加了类型定义和类型检查(不是在运行时)。他们有mypy
:https://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/。