Утиная печать с бесполезными в противном случае заявлениями (удаленно Pythonic) - PullRequest
2 голосов
/ 08 марта 2019

Ниже приведена ошибка типа для поплавков.

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 с плавающей запятой, где stop == start.

>>> divide_factorials(3.3, 3.3)
1

Я мог бы вернуть свой TypeError с помощью isinstance, но это требует от меня точного знания того, что будет и не будет работать в факториале (или любой другой функции, которую я мог бы вызывать). Заманчиво сделать что-то вроде

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

Это кажется более надежным, но менее очевидным. По (я уверен) веским причинам, я не видел этот шаблон раньше. Какая лучшая причина не делать этого?

Я мог бы нарядить его с помощью assert или попробовать / кроме, но

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

# or

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

кажется, во всяком случае, немного более загадочным.

1 Ответ

1 голос
/ 08 марта 2019

На самом деле довольно загадочно вызывать функцию, предназначенную для выполнения вычислений с единственной целью проверки типа. Это делает смысл кода неясным и, следовательно, делает код непифоническим.

Я бы просто повторил ту же явную проверку типа, если это важно. Более того, правильный способ оптимизировать факториальное деление состоит в том, чтобы не вызывать факториальную функцию, а выполнять итерацию от делителя плюс один к дивиденду и вместо этого агрегировать продукт:

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