用其他无用的语句鸭子打字(远程 Pythonic)?

Duck typing with otherwise useless statements (remotely Pythonic)?

下面将引发浮点数的 TypeError。

from math import factorial

def divide_factorials(a, b):
    return factorial(a) / factorial(b)

>>> divide_factorials(6.1, 5)
*** ValueError: factorial() only accepts integral values

在我想进行优化之前,这是完美的。

def divide_factorials(a, b):
    if a == b:
        return 1
    return factorial(a) / factorial(b)

现在我丢失了 TypeError from floats where stop == start。

>>> divide_factorials(3.3, 3.3)
1

我可以用 isinstance 恢复我的 TypeError,但这需要我确切地知道什么会和不会在 factorial(或我可能调用的任何其他函数)中工作。很想做这样的事情

def divide_factorials(a, b):
    # duck type first
    factorial((a + b) % 1)
    if a == b:
        return 1
    return factorial(a) / factorial(b)

这看起来更稳健,但不太明显。出于(我敢肯定)充分的理由,我以前从未见过这种模式。不这样做的最佳理由是什么?

我可以用 assert 或 try/except 修饰它,但是

assert factorial((a + b) % 1) is not None

# or

try:
    factorial((a + b) % 1)
except TypeError:
    raise TypeError

似乎有点神秘。

为了进行类型验证而调用一个旨在执行计算的函数确实很神秘。它使代码的意图不明确,因此使代码不符合 Pythonic。

如果它很重要,我会简单地重复相同的显式类型验证。此外,优化阶乘除法的正确方法是 而不是 调用阶乘函数,而是从除数加一迭代到被除数并聚合乘积:

def divide_factorials(a, b):
    if not all(isinstance(i, int) for i in (a, b)):
        raise TypeError('divide_factorials() only accepts integral values')
    product = 1
    for i in range(b + 1, a + 1):
        product *= i
    return product