Одна реализация для покрытия как одного, так и нескольких значений в Python? - PullRequest
4 голосов
/ 10 марта 2009

Скажем, у вас есть такое значение:

n = 5

и метод, который возвращает его факториал, например:

Факториал (5)

Как вы обрабатываете несколько значений:

nums = [1,2,3,4,5]

Факториал (цифры)

поэтому он возвращает факториалы всех этих значений в виде списка.

Какой самый чистый способ справиться с этим, не написав 2 метода? Есть ли у python хороший способ справиться с подобными ситуациями?

Ответы [ 6 ]

13 голосов
/ 10 марта 2009
def Factorial(arg):
    try:
        it = iter(arg)
    except TypeError:
        pass
    else:
        return [Factorial(x) for x in it]
    return math.factorial(arg)

Если это итеративно, применять рекурсивно. В противном случае действуйте в обычном режиме.

Кроме того, вы можете переместить последний return в блок except.

Если вы уверены, что тело Factorial никогда не поднимет TypeError, его можно упростить до:

def Factorial(arg):
    try:
        return [Factorial(x) for x in arg]
    except TypeError:
        return math.factorial(arg)
9 голосов
/ 10 марта 2009

Понимание списка :

[fac(n) for n in nums]

EDIT:

Извините, я неправильно понял, вы хотите метод, который обрабатывает как последовательности, так и отдельные значения? Я не могу представить, почему вы не сделали бы это двумя способами.

def factorial(n):
    # implement factorial here
    return answer

def factorial_list(nums):
    return [factorial(n) for n in nums]

В качестве альтернативы можно было бы выполнить какую-то проверку типов, которую лучше избегать, если у вас нет для этого какой-либо ужасно убедительной причины.

РЕДАКТИРОВАТЬ 2:

Ответ MizardX лучше, проголосуй за него. Приветствия.

7 голосов
/ 10 марта 2009

Иногда это делается.

def factorial( *args ):
    def fact( n ):
        if n == 0: return 1
        return n*fact(n-1)
    return [ fact(a) for a in args ]

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

>>> factorial(5)
[120]
>>> factorial( 5, 6, 7 )
[120, 720, 5040]
>>> factorial( *[5, 6, 7] )
[120, 720, 5040]
6 голосов
/ 10 марта 2009

Если вы спрашиваете, может ли Python перегрузить метод: нет. Следовательно, выполнение нескольких методов, подобных этому, является довольно непифоническим способом определения метода. Кроме того, соглашение об именах обычно включает имена классов в верхнем регистре и функции / методы в нижнем регистре.

Если вы все равно хотите пойти дальше, самый простой способ - сделать ветку:

def Factorial(arg):
  if getattr(arg, '__iter__', False): # checks if arg is iterable
    return [Factorial(x) for x in arg]
  else:
    # ...

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

def autoMap(f):
    def mapped(arg):
        if getattr(arg, '__iter__', False):
            return [mapped(x) for x in arg]
        else:
            return f(arg)
    return mapped

@autoMap
def fact(x):
    if x == 1 or x == 0:
        return 1
    else:
        return fact(x-1) + fact(x-2)

>>> fact(3)
3
>>> fact(4)
5
>>> fact(5)
8
>>> fact(6)
13
>>> fact([3,4,5,6])
[3, 5, 8, 13]

Хотя более Pythonic способ использовать переменные длины аргументов:

def autoMap2(f):
    def mapped(*arg):
        if len(arg) != 1:
            return [f(x) for x in arg]
        else:
            return f(arg[0])
    return mapped

@autoMap2
def fact(n):
# ...

>>> fact(3,4,5,6)
[3, 5, 8, 13]

Соединение двух в декоратор глубокого отображения:

def autoDeepMap(f):
    def mapped(*args):
        if len(args) != 1:
            return [mapped(x) for x in args]
        elif getattr(args[0], '__iter__', False):
            return [mapped(x) for x in args[0]]
        else:
            return f(args[0])
    return mapped

@autoDeepMap
def fact(n):
# ...

>>> fact(0)
1
>>> fact(0,1,2,3,4,5,6)
[1, 1, 2, 3, 5, 8, 13]
>>> fact([0,1,2,3,4,5,6])
[1, 1, 2, 3, 5, 8, 13]
>>> fact([0,1,2],[3,4,5,6])
[[1, 1, 2], [3, 5, 8, 13]]
>>> fact([0,1,2],[3,(4,5),6])
[[1, 1, 2], [3, [5, 8], 13]]
3 голосов
/ 10 марта 2009

Возможно, вы захотите взглянуть на NumPy / SciPy's векторизация .

В мире с пустяками, учитывая вашу факториальную функцию, вы бы сделали такие вещи, как

  vFactorial=np.vectorize(Factorial)
  vFactorial([1,2,3,4,5])
  vFactorial(6)

хотя обратите внимание, что в последнем случае возвращается одноэлементный массив numpy, а не необработанный int.

3 голосов
/ 10 марта 2009

Или, если вам не нравится синтаксис понимания списка, и вы хотите пропустить новый метод:

def factorial(num):
    if num == 0:
        return 1
    elif num > 0:
        return num * factorial(num - 1)
    else:
        raise Exception("Negative num has no factorial.")

nums = [1, 2, 3, 4, 5]
# [1, 2, 3, 4, 5]

map(factorial, nums)
# [1, 2, 6, 24, 120, 720]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...