例外情况+;发出迭代器结束的信号:为什么Java中的迭代器不好而Python中的迭代器正常?
我真的很困惑:Java中的标准方法是只在“异常”条件下抛出异常,而不使用它们来表示迭代器的结束 示例:有效Java,第57项(“仅在例外情况下使用例外”),以及: 流量控制 我们决不应该造成一个本来可以预防的例外情况。我见过这样的代码:不检查边界,而是假设数据是正确的,然后捕获运行时异常: 下面是一个错误代码的示例(请不要这样编写代码): 然而,在Python中使用此习语是标准的,例如: 异常停止迭代 由迭代器的next()方法引发,表示没有其他值。这是从Exception而非StandardError派生的,因为这在其正常应用程序中不被视为错误例外情况+;发出迭代器结束的信号:为什么Java中的迭代器不好而Python中的迭代器正常?,java,python,exception,Java,Python,Exception,我真的很困惑:Java中的标准方法是只在“异常”条件下抛出异常,而不使用它们来表示迭代器的结束 示例:有效Java,第57项(“仅在例外情况下使用例外”),以及: 流量控制 我们决不应该造成一个本来可以预防的例外情况。我见过这样的代码:不检查边界,而是假设数据是正确的,然后捕获运行时异常: 下面是一个错误代码的示例(请不要这样编写代码): 然而,在Python中使用此习语是标准的,例如: 异常停止迭代 由迭代器的next()方法引发,表示没有其他值。这是从Exception而非StandardE
为什么它对Java不好而对Python好呢?Java有时也会这样:“所有方法的实现都使用而不是返回值。”在这种情况下,无法使用通常的哨兵值,
-1
StopIteration
存在于Python中,用于对任何序列进行简单迭代
使用异常来实现这一点是一种设计选择,它与Java的异常思维并不矛盾。当没有更多元素时调用迭代器的
next()
方法是一种异常情况,在for
循环中在幕后捕获该异常是一种非常简单的方法,可以实现“获取项直到没有剩余项为止”。Python和Java处理异常的方法截然不同。在Python中,异常是正常的。在Python词汇表中查找(请求原谅比请求许可更容易)。还要检查你要说什么
StopIteration只是EAFP的一个例子——继续,从迭代器中获取下一个内容,如果失败,处理错误
如果代码在非本地出口时可读性更好,那么在Python中可以使用异常。你不会开支票,如果事情不顺利,你只会处理失败。这绝对没有什么可耻的,事实上这是被鼓励的。与Java不同
现在对于一个特殊的停止迭代的例子:考虑。 要支持某种类型的
has_next()
方法,生成器必须检查下一个值,在请求2
之前触发打印。必须在迭代器中记住该值(或引发的异常)。如果调用了两次has_next
,则只有第一个会触发副作用。或者下一个值总是可以预先计算的,即使它不是必需的
我发现Python的语义——只在需要下一个值时计算——是最好的选择
当然,Java没有可恢复的生成器,所以很难在这里进行比较。但是有一些轶事证据表明StopIteration比hasNext()概括得更好。对此没有正确或错误的答案。异常处理是一种中立的控制流构造,它的最佳使用取决于上下文和样式
在本例中,Java和Python都做了相同的事情,原因相同:Java.util.Iterator
使用NoTouchElementException
来表示迭代器的结束。这在两种语言中都是很好的风格:出于各种原因,使用特殊的sentinel返回值的替代方法要差得多
作为经验法则,每当编写一个函数,它希望发出不止一种控制流返回时,应该考虑使用异常。这包括异常错误情况,但异常信号的良好使用当然不限于此。
没有什么可以阻止您使用java中的异常,
它看起来很难看,至少对java开发人员来说是这样
主要原因是异常的StackTrace非常昂贵,
而且,java开发人员可能会稍微关心一点
比Python开发人员更关注计算资源的使用
Java也是一种相当“干净”的语言——有些人会说它是原教旨主义的,
这就是为什么它是一门好语言的原因之一。(*见评论)
无论如何。原教旨主义者(和一些普通人)认为,对正常流程使用异常不是正确的方法…:-)
但除此之外,最近的jvm检测到您正在生成大量堆栈跟踪
对于相同的代码点,实际上会在没有异常的情况下“过一段时间”抛出异常,以加快速度。不建议使用它的原因是,在java中,异常处理的处理和恢复成本通常很高。当抛出异常时,它会导致jvm返回正在执行的操作,以提供堆栈跟踪,这对性能来说从来都不是一件好事。简而言之,这是语言误用——通常会有一种更干净、更有效的处理逻辑的方法。考虑下面的代码:
try {
int x = service.getValue();
if(x > 5)
throw new NumberTooBigException("Too Big");
else
throw new NumberTooSmallException("Too Small");
} catch (NumberTooBigException e) {
System.out.println("i think it's too big...");
} catch (NumberTooSmallException e) {
System.out.println("i think it's too small...");
}
更好的方法是使用java的预期控制逻辑:
if(x > 5)
System.out.println("i think it's too big...");
else
System.out.println("i think it's too small...");
从比较这两个代码段可以看出,异常处理有点荒谬——对于示例的意图来说,这是一种过分的处理。对于您发布的示例,更好的方法如下:
public class Antipattern1 {
public static void main(String[] args) {
try {
int i = 0;
while (true) {
System.out.println(args[i++]);
}
} catch (ArrayIndexOutOfBoundsException e) {
// we are done
}
}
}
String[] args = {"one", "two", "three"};
for(String arg : args)
System.out.println(args);
}
异常更适合于出现问题时使用,例如IOException(“设备上没有剩余空间”)、ClassNotFoundException(“找不到要运行的代码”)或NoRouteToHostException(“无法连接到主机”)。Java捕获执行堆栈中的异常非常缓慢:
如果您在控制流传递中使用异常null
作为Throwable
。否则,将通过super
调用的层次结构调用以下标准Java库代码:
public Throwable() {
fillInStackTrace();
}
public synchronized Throwable fillInStackTrace() {
if (stackTrace != null ||
backtrace != null /* Out of protocol state */ ) {
fillInStackTrace(0);
stackTrace = UNASSIGNED_STACK;
}
return this;
}
private native Throwable fillInStackTrace(int dummy);
在Python中也会出现类似的用例,但事实并非如此——在Python中,您并不局限于单一的返回类型
public Throwable() {
fillInStackTrace();
}
public synchronized Throwable fillInStackTrace() {
if (stackTrace != null ||
backtrace != null /* Out of protocol state */ ) {
fillInStackTrace(0);
stackTrace = UNASSIGNED_STACK;
}
return this;
}
private native Throwable fillInStackTrace(int dummy);