How to make reduce function take three parameters in Python? - python

In python,reduce takes a function that accepts only two arguments.
Is there any elegant way to have a reduce that can take more than two arguments?
For example:
from operator import add
list = [1,add,2,add,3,add,4]
func = lambda operand1,operator,operand2: operator(operand1,operand2)
reduce(func,list) # mock reduce. Expected result is 10(1+2+3+4=10)
EDIT:
The reduce3 function is used to calculate the result of the abstract syntax tree(AST).
Every children of AST can be either a node or another tree.
For simplicity, assume that leaf nodes can only be number,addition symbol(+) or subtraction symbol(-).To calculate the result, one intuitive idea is to firstly work out the result of every nodes(can be implemented by recursively calls the method), and then combine them together.
For example, here's a simple AST:
Tree(
Node(1),
Node("+"),
Tree(
Node(2),
"+",
Node(3)
),
Node("-"),
Node(4)
)
After recursion, we get the following result.
If there is a reduce take 3 arguments, we can easily combine the result.
Tree(
Node(1),
Node("+"),
Node(5)
Node("-"),
Node(4)
)

This gives the exact result without 3 arguments.
It creates a partial function on each argument if arguments are like 1, add and calls the function if arguments are like partial,2
from operator import add
from functools import reduce, partial
lst = [1,add,2,add,3,add,4]
def operation_reduce(x, y):
if callable(x):
return x(y)
else:
return partial(y, x)
print(reduce(operation_reduce, lst)) #10

reduce can't be changed, but you could provide a callable that handles the differing inputs:
from functools import reduce
from operator import add, sub
L = [1, add, 2, add, 3, add, 4, sub]
class Reducer:
def __init__(self):
self.pending = 0
def __call__(self, total, value):
if callable(value):
total = value(total, self.pending)
else:
self.pending = value
return total
func = Reducer()
result = reduce(func, L, 0)
print(result)
Or you could batch the inputs in tuples, and use a callable that could handle those
def reducer(total, next_):
value, operation = next_
return operation(total, value)
result = reduce(reducer, zip(L[::2], L[1::2]), 0)
print(result)

I'd be interested to hear what is your desired use case as I believe reduce taking more than 2 arguments is not elegant, period. Here's my rationale (though it's a bit hard arguing in abstractness, with no concrete use case):
Assume the reducing operation takes 3 arguments as in your example. Let's also make initial accumulator explicit, reduce3(func,list,acc). Then in every call to func:
first argument is accumulator as usual
second argument is always an element at the odd position in the list
third argument is always at an even position in the list
Even positioned elements from the list are treated by the func differently than odd positioned elements. We can ever really need such a reduce3 function if the elements at odd and even positions are of different type!*
It is the case in the example. Mixing elements of different type in one list requires more caution not to make a mistake (mess up the order of some 2 elements and boom the reduce3 breaks) and IMO should be avoided in general.
Modifying your example:
list = [2,3,4] # we'l include 1 as initial acc
ops = [add, add, add]
func = lambda acc, pair : pair[0](acc, pair[1])
reduce(func, zip(ops, list), 1) # result is 10(1+2+3+4=10)
Reducing over a list of tuples is a general solution to your question. If mathematical expressions is what you want to evaluate you should consider their tree structure, which gets lost when collapsed to a list.
Edit:
Indeed you want to evaluate mathematical expressions. Consider changing the way you are parsing these expressions so that for the example the AST looks like:
Tree("-",
Tree("+",
Node(1),
Tree("+",
Node(2),
Node(3))),
Node(4))
# -
# / \
# + 4
# / \
# 1 +
# / \
# 2 3
That is internal nodes are always tagged with binary operation and it is only leafs that carry numbers. Now reducing tree to a value is trivial but at a cost of parsing doing most of the work.
# Here assuming some specifics of the Tree and Node classes.
# strToOpt is mapping of operation symbols to functions.
def evaluate(expr): # expr : Tree or Node
if (isinstance(expr, Node)): # expr : Node
return expr.x
else: # expr : Tree
return strToOp[expr.op](evaluate(expr.left), evaluate(expr.right))
If you allow for non-binary tree nodes then swap the "else" branch with reduce like reduce(strToOpt[expr.op], expr.children).
I understand that this is very different approach. Reduce3 shouldn't be that hard to implement if you want to stick to it, right? For some help on parsing math from scratch in python you could look at the old project of mine BUT it's amateur code written by a student (me). On the other hand would be unfair not to share, here you go: parsing
*Not necessarily of different type in a programming sense, but certainly of different type or meaning to us.

Related

Quicksort ascending and descendding python

I am trying to implement quicksort in place in ascending and descending order. The pivot element is the middle one. If the list is length 2 the pivot element is the first one. if the list length is even the pivot element is the last number of the first half of list. I have written a function which is called
quickSort(L, ascending True) L = List, true=ascending, false=descending
quickSort(testList1,False)
def quickSort(L, ascending = True):
print('Quicksort, Parameter L:')
print(L)
def quicksort(listToSort,lowIndex,highIndex):
if ((highIndex - lowIndex) > 0):
p = partition(listToSort,lowIndex,highIndex)
quicksort(listToSort,lowIndex,p-1)
quicksort(listToSort,p+1,highIndex)
def partition(listToSort, lowIndex, highIndex):
divider = lowIndex
pivot = highIndex
for i in range(lowIndex,highIndex):
if (listToSort[i] < listToSort[pivot]):
listToSort[i], listToSort[divider] = listToSort[divider],listToSort[i]
divider +=1
listToSort[pivot], listToSort[divider] = listToSort[divider], listToSort[pivot]
return divider
testList1 = [1,0,2,3,4,6,5,8,9,7]
testList2 = list([3.14159 , 1./127, 2.718 , 1.618 , -23., 3.14159])
testList3 = [10,9,8,7,6,5,4,3,2,1]
quicksort(testList1,0,9)
print (testList1)
quicksort(testList2,0,5)
print (testList2)
quicksort(testList3,0,9)
print (testList3)
quickSort(testList1,False)
The idea behind quick sort is partitioning a list into values that are less than a target value and the values greater than the target value. I used Haskell for the algorithm because algorithms can be directly expressed in Haskell. I then tried to translate it into Python. This turned into was more coding than I am accustomed to, to accomplish minor tasks.
In Haskell, it was necessary to import partition from Data.List
partition takes a list and produces 2 parts according to a supplied predicate. The predicate, in this case, is only <=nv because it is implied that each successive element of a list will be used as an argument. Haskell, like Python, can assign multiple values at a time.
The two parts of the partition output are bound to variables 'b' for beginning and 'e' for end because I am not very creative.
The word where is as in math.
b is concatenated with the insertion value which is then concatenated to 'e'
Haskell has functions that operate on functions. I give this function to foldr with required parameters. foldr uses the supplied function recursively with successive accumulated values.
The function is
ft nv ls = b++[nv]++e where (b,e) = partition (<=nv) ls
Executing it with foldr and it sorts the list. Place the reverse function in front of it for a reversed sorted list.
foldr ft [] [5,4,3,2,1,0,9,8,7,6]
Edit 6-21-2018\
I was trying to make the Python version recursive but failed. In Python, the iterative version came too easily. The two filter functions do the same thing as the partition function in Haskell.
def isort(ls):
nls=ls
for i in ls:
nls= filter(lambda x: x<i,nls) +[i]+ filter(lambda x: x>i,nls)
return(nls)

Algorithm to reverse list items in place?

I'm learning about data structures and algorithm efficiency in my CS class right now, and I've created an algorithm to return a new list of items that in reverse order from the original list. I'm trying to figure out how to do this in place with Python lists using recursion, but the solution is eluding me. Here is my code:
def reverse_list(in_list):
n = len(in_list)
print('List is: ', in_list)
if n <= 2:
in_list[0], in_list[-1] = in_list[-1], in_list[0]
return in_list
else:
first = [in_list[0]]
last = [in_list[-1]]
temp = reverse_list(in_list[1:-1])
in_list = last + temp + first
print('Now list is: ', in_list)
return in_list
if __name__ == '__main__':
list1 = list(range(1, 12))
list2 = reverse_list(list1)
print(list2)
As a side note, this is O(n) in average case due to it being n/2 right?
You really don't want to be using recursion here as it doesn't really simplify the problem for the extra overhead involved with the function calls:
list3 = list(range(1, 10))
for i in range(len(list3)/2):
list3[i], list3[len(list3)-i-1] = list3[len(list3)-i-1], list3[i]
print(list3)
if you wanted to approach it using recursion, you'd pass in an index counter to each call of your function till you had gotten half way through the list. This would give you O(n/2) runtime.
Your n <= 2 case is good. It is "in-place" in both of the usual senses. It modifies the same list as is passed in, and it only uses a constant amount of memory in addition to the list passed in.
Your general case is where you have problems. It uses a lot of extra memory (n/2 levels of recursion, each of which holds two lists across the recursive call, so all those lists at all levels have to exist at once), and it does not modify the list passed in (rather, it returns a new list).
I don't want to do too much for you, but the key to solving this is to pass one (or two if you prefer) extra parameter(s) into the recursive step. Either an optional parameter to your function, or define a recursive helper function that has them. Use this to indicate what region of the list to reverse in-place.
As someone mentioned in comments, CPython doesn't have a tail-recursion optimization. This means that no algorithm that uses call-recursion like this is ever going to be truly "in-place", since it will use linear amounts of stack. But "in-place" in the sense of "modifies the original list instead of returning a new one" is certainly doable.

How can I call a sequence of functions until the return value meets some condition?

Sometimes I find myself writing code like this:
def analyse(somedata):
result = bestapproach(somedata)
if result:
return result
else:
result = notasgood(somedata)
if result:
return result
else:
result = betterthannothing(somedata)
if result:
return result
else:
return None
That's pretty ugly. Of course, some people like to leave off some of that syntax:
def analyse(somedata):
result = bestapproach(somedata)
if result:
return result
result = notasgood(somedata)
if result:
return result
result = betterthannothing(somedata)
if result:
return result
But that's not much of an improvement; there's still a ton of duplicated code here, and it's still ugly.
I looked into using the built-in iter() with a sentinel value, but in this case the None value is being used to signal that the loop should keep going, as opposed to a sentinel which is used to signal that the loop should terminate.
Are there any other (sane) techniques in Python for implementing this sort of "keep trying until you find something that works" pattern?
I should clarify that "return value meets some condition" is not limited to cases where the condition is if bool(result) is True as in the example. It could be that the list of possible analysis functions each produce some coefficient measuring the degree of success (e.g. an R-squared value), and you want to set a minimum threshold for acceptance. Therefore, a general solution should not inherently rely on the truth value of the result.
Option #1: Using or
When the number of total functions is a) known, and b) small, and the test condition is based entirely on the truth value of the return, it's possible to simply use or as Grapsus suggested:
d = 'somedata'
result = f1(d) or f2(d) or f3(d) or f4(d)
Because Python's boolean operators short-circuit, the functions are executed from right to left until one of them produces a return value evaluated as True, at which point the assignment is made to result and the remaining functions are not evaluated; or until you run out of functions, and result is assigned False.
Option #2: Using generators
When the number of total functions is a) unknown, or b) very large, a one-liner generator comprehension method works, as Bitwise suggested:
result = (r for r in (f(somedata) for f in functions) if <test-condition>).next()
This has the additional advantage over option #1 that you can use any <test-condition> you wish, instead of relying only on truth value. Each time .next() is called:
We ask the outer generator for its next element
The outer generator asks the inner generator for its next element
The inner generator asks for an f from functions and tries to evaluate f(somedata)
If the expression can be evaluated (i.e., f is a function and somedata is a valid arugment), the inner generator yields the return value of f(somedata) to the outer generator
If <test-condition> is satisfied, the outer generator yields the return value of f(somedata) and we assign it as result
If <test-condition> was not satisfied in step 5, repeat steps 2-4
A weakness of this method is that nested comprehensions can be less intuitive than their multi-line equivalents. Also, if the inner generator is exhausted without ever satisfying the test condition, .next() raises a StopIteration which must be handled (in a try-except block) or prevented (by ensuring the last function will always "succeed").
Option #3: Using a custom function
Since we can place callable functions in a list, one option is to explicitly list the functions you want to "try" in the order they should be used, and then iterate through that list:
def analyse(somedata):
analysis_functions = [best, okay, poor]
for f in analysis_functions:
result = f(somedata)
if result:
return result
Advantages: Fixes the problem of repeated code, it's more clear that you're engaged in an iterative process, and it short-circuits (doesn't continue executing functions after it finds a "good" result).
This could also be written with Python's for ... else syntax:*
def analyse(somedata):
analysis_functions = [best, okay, poor]
for f in analysis_functions:
result = f(somedata)
if result:
break
else:
return None
return result
The advantage here is that the different ways to exit the function are identified, which could be useful if you want complete failure of the analyse() function to return something other than None, or to raise an exception. Otherwise, it's just longer and more esoteric.
*As described in "Transforming Code into Beautiful, Idiomatic Python", starting #15:50.
If the number of functions is not too high, why not use the or operator ?
d = 'somedata'
result = f1(d) or f2(d) or f3(d) or f4(d)
It will only apply the functions until one of them returns something not False.
This is pretty pythonic:
result = (i for i in (f(somedata) for f in funcs) if i is not None).next()
The idea is to use generators so you do lazy evaluation instead of evaluating all functions. Note that you can change the condition/funcs to be whatever you like, so this is more robust than the or solution proposed by Grapsus.
This is a good example why generators are powerful in Python.
A more detailed description of how this works:
We ask this generator for a single element. The outer generator then asks the inner generator (f(d) for f in funcs) for a single element, and evaluates it. If it passes the condition then we are done and it exits, otherwise it continues asking the inner generator for elements.

Using Python, How can I evaluate an expression in the form of prefix notation compacted?

I am currently working on the python problemsets on a website called singpath. The question is:
Prefix Evaluation
Create a function that evaluates the arithmetic expression in the form of prefix notation without spaces or syntax errors. The expression is given as a string, all the numbers in the expression are integer 0~9, and the operators are +(addition), -(subtraction), *(multiplication), /(division), %(modulo), which operate just the same as those in Python.
Prefix notation, also known as Polish notation, is a form of notation for logic, arithmetic, and algebra. it places operators to the left of their operands. If the arity of the operators is fixed, the result is a syntax lacking parentheses or other brackets that can still be parsed without ambiguity.
This seems simple enough but the string is condensed with no spaces in the input to splice out the data. How could I separate the data from the string without importing modules? Furthermore how could I use the results of the data to solve the given equation? Also please keep in minf that Singpath solutions must be in ONE function that cannot use methods that couldn't be found in the standard python library. This also includes functions declared within the solution :S
Examples:
>>> eval_prefix("+34")
7
>>> eval_prefix("*−567")
-7
>>> eval_prefix("-*33+2+11")
5
>>> eval_prefix("-+5*+1243")
14
>>> eval_prefix("*+35-72")
40
>>> eval_prefix("%3/52")
1
See my point no spaces D:
OK, not as snazzy as alex jordan's lamba/reduce solution, but it doesn't choke on garbage input. It's sort of a recursive descent parser meets bubble sort abomination (I'm thinking it could be a little more efficient when it finds a solvable portion than just jumping back to the start. ;)
import operator
def eval_prefix(expr):
d = {'+': operator.add,
'-': operator.sub,
'*': operator.mul,
'/': operator.div, # for 3.x change this to operator.truediv
'%': operator.mod}
for n in range(10):
d[str(n)] = n
e = list(d.get(e, None) for e in expr)
i = 0
while i + 3 <= len(e):
o, l, r = e[i:i+3]
if type(o) == type(operator.add) and type(l) == type(r) == type(0):
e[i:i+3] = [o(l, r)]
i = 0
else:
i += 1
if len(e) != 1:
print 'Error in expression:', expr
return 0
else:
return e[0]
def test(s, v):
r = eval_prefix(s)
print s, '==', v, r, r == v
test("+34", 7)
test("*-567", -7)
test("-*33+2+11", 5)
test("-+5*+1243", 14)
test("*+35-72", 40)
test("%3/52", 1)
test("****", 0)
test("-5bob", 10)
I think the crucial bit here is "all the numbers in the expression are integer 0~9". All numbers are single digit. You don't need spaces to find out where one number ends and the next one starts. You can access the numbers directly by their string index, as lckknght said.
To convert the characters in the string into integers for calculation, use ord(ch) - 48 (because "0" has the ASCII code 48). So, to get the number stored in position 5 of input, use ord(input[5]) - 48.
To evaluate nested expressions, you can call your function recursively. The crucial assumption here is that there are always exactly two operants to an operator.
Your "one function" limitation isn't as bad as you think. Python allows defining functions inside functions. In the end, a function definition is nothing more than assigning the function to a (usually new) variable. In this case, I think you will want to use recursion. While that can also be done without an extra function, you may find it easier to define an extra recursion function for it. This is no problem for your limits:
def eval_prefix (data):
def handle_operator (operator, rest):
# You fill this in.
# and this, too.
That should be enough of a hint (if you want to use a recursive approach).
Well, one-liner fits in? Reduce in python3 is hidden in functools
Somewhat lispy :)
eval_prefix = lambda inp:\
reduce(lambda stack, symbol:\
(
(stack+[symbol]) if symbol.isdigit() \
else \
(
stack[:-2]+\
[str(
eval(
stack[-1]+symbol+stack[-2]
)
)
]
)
), inp[::-1], [])[0]
The hint that you are most likely looking for is "strings are iterable":
def eval_prefix(data):
# setup state machine
for symbol_ in data:
# update state machine
Separating the elements of the string is easy. All elements are a single character long, so you can directly iterate over (or index) the string to get at each one. Or if you want to be able to manipulate the values, you could passing the string to the list constructor.
Here are some examples of how this can work:
string = "*-567"
# iterating over each character, one at a time:
for character in string:
print(character) # prints one character from the string per line
# accessing a specific character by index:
third_char = string[2] # note indexing is zero-based, so 3rd char is at index 2
# transform string to list
list_of_characters = list(string) # will be ["*", "-", "5", "6", "7"]
As for how to solve the equation, I think there are two approaches.
One is to make your function recursive, so that each call evaluates a single operation or literal value. This is a little tricky, since you're only supposed to use one function (it would be much easier if you could have a recursive helper function that gets called with a different API than the main non-recursive function).
The other approach is to build up a stack of values and operations that you're waiting to evaluate while taking just a single iteration over the input string. This is probably easier given the one-function limit.

Declaring Unknown Type Variable in Python?

I have a situation in Python(cough, homework) where I need to multiply EACH ELEMENT in a given list of objects a specified number of times and return the output of the elements. The problem is that the sample inputs given are of different types. For example, one case may input a list of strings whose elements I need to multiply while the others may be ints. So my return type needs to vary. I would like to do this without having to test what every type of object is. Is there a way to do this? I know in C# i could just use "var" but I don't know if such a thing exists in Python?
I realize that variables don't have to be declared, but in this case I can't see any way around it. Here's the function I made:
def multiplyItemsByFour(argsList):
output = ????
for arg in argsList:
output += arg * 4
return output
See how I need to add to the output variable. If I just try to take away the output assignment on the first line, I get an error that the variable was not defined. But if I assign it a 0 or a "" for an empty string, an exception could be thrown since you can't add 3 to a string or "a" to an integer, etc...
Here are some sample inputs and outputs:
Input: ('a','b') Output: 'aaaabbbb'
Input: (2,3,4) Output: 36
Thanks!
def fivetimes(anylist):
return anylist * 5
As you see, if you're given a list argument, there's no need for any assignment whatsoever in order to "multiply it a given number of times and return the output". You talk about a given list; how is it given to you, if not (the most natural way) as an argument to your function? Not that it matters much -- if it's a global variable, a property of the object that's your argument, and so forth, this still doesn't necessitate any assignment.
If you were "homeworkically" forbidden from using the * operator of lists, and just required to implement it yourself, this would require assignment, but no declaration:
def multiply_the_hard_way(inputlist, multiplier):
outputlist = []
for i in range(multiplier):
outputlist.extend(inputlist)
return outputlist
You can simply make the empty list "magicaly appear": there's no need to "declare" it as being anything whatsoever, it's an empty list and the Python compiler knows it as well as you or any reader of your code does. Binding it to the name outputlist doesn't require you to perform any special ritual either, just the binding (aka assignment) itself: names don't have types, only objects have types... that's Python!-)
Edit: OP now says output must not be a list, but rather int, float, or maybe string, and he is given no indication of what. I've asked for clarification -- multiplying a list ALWAYS returns a list, so clearly he must mean something different from what he originally said, that he had to multiply a list. Meanwhile, here's another attempt at mind-reading. Perhaps he must return a list where EACH ITEM of the input list is multiplied by the same factor (whether that item is an int, float, string, list, ...). Well then:
define multiply_each_item(somelist, multiplier):
return [item * multiplier for item in somelist]
Look ma, no hands^H^H^H^H^H assignment. (This is known as a "list comprehension", btw).
Or maybe (unlikely, but my mind-reading hat may be suffering interference from my tinfoil hat, will need to go to the mad hatter's shop to have them tuned) he needs to (say) multiply each list item as if they were the same type as the first item, but return them as their original type, so that for example
>>> mystic(['zap', 1, 23, 'goo'], 2)
['zapzap', 11, 2323, 'googoo']
>>> mystic([23, '12', 15, 2.5], 2)
[46, '24', 30, 4.0]
Even this highly-mystical spec COULD be accomodated...:
>>> def mystic(alist, mul):
... multyp = type(alist[0])
... return [type(x)(mul*multyp(x)) for x in alist]
...
...though I very much doubt it's the spec actually encoded in the mysterious runes of that homework assignment. Just about ANY precise spec can be either implemented or proven to be likely impossible as stated (by requiring you to solve the Halting Problem or demanding that P==NP, say;-). That may take some work ("prove the 4-color theorem", for example;-)... but still less than it takes to magically divine what the actual spec IS, from a collection of mutually contradictory observations, no examples, etc. Though in our daily work as software developer (ah for the good old times when all we had to face was homework!-) we DO meet a lot of such cases of course (and have to solve them to earn our daily bread;-).
EditEdit: finally seeing a precise spec I point out I already implemented that one, anyway, here it goes again:
def multiplyItemsByFour(argsList):
return [item * 4 for item in argsList]
EditEditEdit: finally/finally seeing a MORE precise spec, with (luxury!-) examples:
Input: ('a','b') Output: 'aaaabbbb' Input: (2,3,4) Output: 36
So then what's wanted it the summation (and you can't use sum as it wouldn't work on strings) of the items in the input list, each multiplied by four. My preferred solution:
def theFinalAndTrulyRealProblemAsPosed(argsList):
items = iter(argsList)
output = next(items, []) * 4
for item in items:
output += item * 4
return output
If you're forbidden from using some of these constructs, such as built-ins items and iter, there are many other possibilities (slightly inferior ones) such as:
def theFinalAndTrulyRealProblemAsPosed(argsList):
if not argsList: return None
output = argsList[0] * 4
for item in argsList[1:]:
output += item * 4
return output
For an empty argsList, the first version returns [], the second one returns None -- not sure what you're supposed to do in that corner case anyway.
Very easy in Python. You need to get the type of the data in your list - use the type() function on the first item - type(argsList[0]). Then to initialize output (where you now have ????) you need the 'zero' or nul value for that type. So just as int() or float() or str() returns the zero or nul for their type so to will type(argsList[0])() return the zero or nul value for whatever type you have in your list.
So, here is your function with one minor modification:
def multiplyItemsByFour(argsList):
output = type(argsList[0])()
for arg in argsList:
output += arg * 4
return output
Works with::
argsList = [1, 2, 3, 4] or [1.0, 2.0, 3.0, 4.0] or "abcdef" ... etc,
Are you sure this is for Python beginners? To me, the cleanest way to do this is with reduce() and lambda, both of which are not typical beginner tools, and sometimes discouraged even for experienced Python programmers:
def multiplyItemsByFour(argsList):
if not argsList:
return None
newItems = [item * 4 for item in argsList]
return reduce(lambda x, y: x + y, newItems)
Like Alex Martelli, I've thrown in a quick test for an empty list at the beginning which returns None. Note that if you are using Python 3, you must import functools to use reduce().
Essentially, the reduce(lambda...) solution is very similar to the other suggestions to set up an accumulator using the first input item, and then processing the rest of the input items; but is simply more concise.
My guess is that the purpose of your homework is to expose you to "duck typing". The basic idea is that you don't worry about the types too much, you just worry about whether the behaviors work correctly. A classic example:
def add_two(a, b):
return a + b
print add_two(1, 2) # prints 3
print add_two("foo", "bar") # prints "foobar"
print add_two([0, 1, 2], [3, 4, 5]) # prints [0, 1, 2, 3, 4, 5]
Notice that when you def a function in Python, you don't declare a return type anywhere. It is perfectly okay for the same function to return different types based on its arguments. It's considered a virtue, even; consider that in Python we only need one definition of add_two() and we can add integers, add floats, concatenate strings, and join lists with it. Statically typed languages would require multiple implementations, unless they had an escape such as variant, but Python is dynamically typed. (Python is strongly typed, but dynamically typed. Some will tell you Python is weakly typed, but it isn't. In a weakly typed language such as JavaScript, the expression 1 + "1" will give you a result of 2; in Python this expression just raises a TypeError exception.)
It is considered very poor style to try to test the arguments to figure out their types, and then do things based on the types. If you need to make your code robust, you can always use a try block:
def safe_add_two(a, b):
try:
return a + b
except TypeError:
return None
See also the Wikipedia page on duck typing.
Python is dynamically typed, you don't need to declare the type of a variable, because a variable doesn't have a type, only values do. (Any variable can store any value, a value never changes its type during its lifetime.)
def do_something(x):
return x * 5
This will work for any x you pass to it, the actual result depending on what type the value in x has. If x contains a number it will just do regular multiplication, if it contains a string the string will be repeated five times in a row, for lists and such it will repeat the list five times, and so on. For custom types (classes) it depends on whether the class has an operation defined for the multiplication operator.
You don't need to declare variable types in python; a variable has the type of whatever's assigned to it.
EDIT:
To solve the re-stated problem, try this:
def multiplyItemsByFour(argsList):
output = argsList.pop(0) * 4
for arg in argsList:
output += arg * 4
return output
(This is probably not the most pythonic way of doing this, but it should at least start off your output variable as the right type, assuming the whole list is of the same type)
You gave these sample inputs and outputs:
Input: ('a','b') Output: 'aaaabbbb' Input: (2,3,4) Output: 36
I don't want to write the solution to your homework for you, but I do want to steer you in the correct direction. But I'm still not sure I understand what your problem is, because the problem as I understand it seems a bit difficult for an intro to Python class.
The most straightforward way to solve this requires that the arguments be passed in a list. Then, you can look at the first item in the list, and work from that. Here is a function that requires the caller to pass in a list of two items:
def handle_list_of_len_2(lst):
return lst[0] * 4 + lst[1] * 4
Now, how can we make this extend past two items? Well, in your sample code you weren't sure what to assign to your variable output. How about assigning lst[0]? Then it always has the correct type. Then you could loop over all the other elements in lst and accumulate to your output variable using += as you wrote. If you don't know how to loop over a list of items but skip the first thing in the list, Google search for "python list slice".
Now, how can we make this not require the user to pack up everything into a list, but just call the function? What we really want is some way to accept whatever arguments the user wants to pass to the function, and make a list out of them. Perhaps there is special syntax for declaring a function where you tell Python you just want the arguments bundled up into a list. You might check a good tutorial and see what it says about how to define a function.
Now that we have covered (very generally) how to accumulate an answer using +=, let's consider other ways to accumulate an answer. If you know how to use a list comprehension, you could use one of those to return a new list based on the argument list, with the multiply performed on each argument; you could then somehow reduce the list down to a single item and return it. Python 2.3 and newer have a built-in function called sum() and you might want to read up on that. [EDIT: Oh drat, sum() only works on numbers. See note added at end.]
I hope this helps. If you are still very confused, I suggest you contact your teacher and ask for clarification. Good luck.
P.S. Python 2.x have a built-in function called reduce() and it is possible to implement sum() using reduce(). However, the creator of Python thinks it is better to just use sum() and in fact he removed reduce() from Python 3.0 (well, he moved it into a module called functools).
P.P.S. If you get the list comprehension working, here's one more thing to think about. If you use a list comprehension and then pass the result to sum(), you build a list to be used once and then discarded. Wouldn't it be neat if we could get the result, but instead of building the whole list and then discarding it we could just have the sum() function consume the list items as fast as they are generated? You might want to read this: Generator Expressions vs. List Comprehension
EDIT: Oh drat, I assumed that Python's sum() builtin would use duck typing. Actually it is documented to work on numbers, only. I'm disappointed! I'll have to search and see if there were any discussions about that, and see why they did it the way they did; they probably had good reasons. Meanwhile, you might as well use your += solution. Sorry about that.
EDIT: Okay, reading through other answers, I now notice two ways suggested for peeling off the first element in the list.
For simplicity, because you seem like a Python beginner, I suggested simply using output = lst[0] and then using list slicing to skip past the first item in the list. However, Wooble in his answer suggested using output = lst.pop(0) which is a very clean solution: it gets the zeroth thing on the list, and then you can just loop over the list and you automatically skip the zeroth thing. However, this "mutates" the list! It's better if a function like this does not have "side effects" such as modifying the list passed to it. (Unless the list is a special list made just for that function call, such as a *args list.) Another way would be to use the "list slice" trick to make a copy of the list that has the first item removed. Alex Martelli provided an example of how to make an "iterator" using a Python feature called iter(), and then using iterator to get the "next" thing. Since the iterator hasn't been used yet, the next thing is the zeroth thing in the list. That's not really a beginner solution but it is the most elegant way to do this in Python; you could pass a really huge list to the function, and Alex Martelli's solution will neither mutate the list nor waste memory by making a copy of the list.
No need to test the objects, just multiply away!
'this is a string' * 6
14 * 6
[1,2,3] * 6
all just work
Try this:
def timesfourlist(list):
nextstep = map(times_four, list)
sum(nextstep)
map performs the function passed in on each element of the list(returning a new list) and then sum does the += on the list.
If you just want to fill in the blank in your code, you could try setting object=arglist[0].__class__() to give it the zero equivalent value of that class.
>>> def multiplyItemsByFour(argsList):
output = argsList[0].__class__()
for arg in argsList:
output += arg * 4
return output
>>> multiplyItemsByFour('ab')
'aaaabbbb'
>>> multiplyItemsByFour((2,3,4))
36
>>> multiplyItemsByFour((2.0,3.3))
21.199999999999999
This will crash if the list is empty, but you can check for that case at the beginning of the function and return whatever you feel appropriate.
Thanks to Alex Martelli, you have the best possible solution:
def theFinalAndTrulyRealProblemAsPosed(argsList):
items = iter(argsList)
output = next(items, []) * 4
for item in items:
output += item * 4
return output
This is beautiful and elegant. First we create an iterator with iter(), then we use next() to get the first object in the list. Then we accumulate as we iterate through the rest of the list, and we are done. We never need to know the type of the objects in argsList, and indeed they can be of different types as long as all the types can have operator + applied with them. This is duck typing.
For a moment there last night I was confused and thought that you wanted a function that, instead of taking an explicit list, just took one or more arguments.
def four_x_args(*args):
return theFinalAndTrulyRealProblemAsPosed(args)
The *args argument to the function tells Python to gather up all arguments to this function and make a tuple out of them; then the tuple is bound to the name args. You can easily make a list out of it, and then you could use the .pop(0) method to get the first item from the list. This costs the memory and time to build the list, which is why the iter() solution is so elegant.
def four_x_args(*args):
argsList = list(args) # convert from tuple to list
output = argsList.pop(0) * 4
for arg in argsList:
output += arg * 4
return output
This is just Wooble's solution, rewritten to use *args.
Examples of calling it:
print four_x_args(1) # prints 4
print four_x_args(1, 2) # prints 12
print four_x_args('a') # prints 'aaaa'
print four_x_args('ab', 'c') # prints 'ababababcccc'
Finally, I'm going to be malicious and complain about the solution you accepted. That solution depends on the object's base class having a sensible null or zero, but not all classes have this. int() returns 0, and str() returns '' (null string), so they work. But how about this:
class NaturalNumber(int):
"""
Exactly like an int, but only values >= 1 are possible.
"""
def __new__(cls, initial_value=1):
try:
n = int(initial_value)
if n < 1:
raise ValueError
except ValueError:
raise ValueError, "NaturalNumber() initial value must be an int() >= 1"
return super(NaturalNumber, cls).__new__ (cls, n)
argList = [NaturalNumber(n) for n in xrange(1, 4)]
print theFinalAndTrulyRealProblemAsPosed(argList) # prints correct answer: 24
print NaturalNumber() # prints 1
print type(argList[0])() # prints 1, same as previous line
print multiplyItemsByFour(argList) # prints 25!
Good luck in your studies, and I hope you enjoy Python as much as I do.

Categories

Resources