A single implementation to cover both single and multiple values ​​in Python?

Say you have a value like this:

n = 5

and a method that returns a factorial, for example:

Factorial (5)

How do you handle multiple values:

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

Factorial (nums)

therefore, it returns the factorials of all these values ​​as a list.

What is the cleanest way to handle this without writing 2 methods? Is python a good way to handle such situations?

+4
source share
6 answers

This is done sometimes.

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

It provides an almost magical function that works with both simple values ​​and sequences.

 >>> factorial(5) [120] >>> factorial( 5, 6, 7 ) [120, 720, 5040] >>> factorial( *[5, 6, 7] ) [120, 720, 5040] 
+7
source
 def Factorial(arg): try: it = iter(arg) except TypeError: pass else: return [Factorial(x) for x in it] return math.factorial(arg) 

If it repeats, apply recursively. Otherwise, proceed normally.

Alternatively, you can move the last return to the except block.

If you are sure that the Factorial body will never raise a TypeError , it can be simplified:

 def Factorial(arg): try: return [Factorial(x) for x in arg] except TypeError: return math.factorial(arg) 
+13
source

List Accounting :

 [fac(n) for n in nums] 

EDIT:

Sorry, I misunderstood, do you need a method that processes both sequences and single values? I can’t imagine why you wouldn’t do this with two methods.

 def factorial(n): # implement factorial here return answer def factorial_list(nums): return [factorial(n) for n in nums] 

An alternative would be to do some type checking, which is best avoided unless you have some terribly convincing reason for this.

EDIT 2:

MizardX's answer is better, vote for it. Greetings.

+9
source

If you ask if Python can overload the method: no. Therefore, executing several methods like this is a rather non-pythonic way of defining a method. In addition, a naming convention typically refers to upper case class names, as well as lower case functions / methods.

If you want to go forward, the easiest way would be to simply create a branch:

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

Or, if you feel weird, you can make a decorator that does this with any function:

 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] 

Although the more Pythonic way is to use variable argument lengths:

 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] 

Put the two together in the deep image decorator:

 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]] 
+6
source

Or, if you don’t like the syntax for list comprehension, and you want to skip the new method:

 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] 
+3
source

You might want to take a look at NumPy / SciPy vectorize .

In the numpy world, given your single phase factorial function, you are doing something like

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

although note that the latter case returns a one-dimensional numpy elementary array, not raw int.

+3
source

All Articles