I'm new to python and was having some issues with a function that I had defined. I had decided to use *numbers (*args) in my function so that my function could take any amount of arguments. However, in using *numbers when defining my function, it seems that my arguments are treated as a string rather than an integer. As you can see below, I receive the output 4, 4, 4, 4, 4, 4, 4, 4 when inputting the argument "4".
The output that I was looking for was actually 32 however I can't seem to get this output as I can't get my arguments to be treated like an integer. I have tried including int(numbers) to my function but to no avail. Are there any slight changes I can make to get my desired output?
def multiply_machine(*numbers):
multiplied_number=3*numbers
m_numbers=5*numbers
return multiplied_number + m_numbers
print(multiply_machine(4))
OUT[1] : (4, 4, 4, 4, 4, 4, 4, 4)
The input is understood as a tuple.
print(multiply_machine(4))
def multiply_machine(numbers=(4,)):
multiplied_number=3*(4,)
m_numbers=5*(4,)
return (4, 4, 4) + (4, 4, 4, 4, 4)
Thus, to have expected results, you should work with it as with a tuple. What was your intention to actually achieve is not clear to me. You might want to edit your question to be more clear.
You can take advantage of 'iter' attribute provided by the '*numbers'
Option 1
def multiply_machine(*numbers): # preferable use args
print(hasattr(numbers, '__iter__'))
multiply_machine(4,3,4,4,5)
Output
True
so you can iterate through
def multiply_machine(*numbers): # preferable use args
multiplied_number = [3 * x for x in numbers]
m_numbers = [5 * x for x in numbers]
summed = [x + y for x, y in zip(multiplied_number,m_numbers)]
return summed
print(multiply_machine(4))
print(multiply_machine(4,2,1))
Output: Note it returns as
[32]
[32, 16, 8]
Option 2:
You can convert your function into a generator like so:
def multiply_machine(*numbers): # preferable use args
yield from numbers
for i in multiply_machine(4,2,3,4,5):
summed = (5*i) + (3*i)
print(summed, end=' ')
Output
32 16 24 32 40
Related
I understand that when defining a function, you can provide a parameter, like so:
def func(lst):
# code
I also understand that when you are defining a function, you can provide multiple parameters using the *args syntax, like so:
def func(*lst):
# code
I have a problem where I have to define a function that sorts a list and removes any duplicates.
Here's what I did:
def func(lst):
return sorted(set(lst))
The website that I was doing this practice problem (edabit.com) tested my code, like so:
Test.assert_equals(func([1, 3, 3, 5, 5]), [1, 3, 5])
The test ran successfully, and my code was correct. But here's where I got confused, the test provided multiple parameters (1, 3, 3, 5, 5), I didn't use the *args syntax, yet somehow it ran successfully.
Isn't it supposed to give me an error, saying something like func() takes exactly 1 argument (5 given)?
When I provided the *args syntax, it told me TypeError: unhashable type:'list'
My guess is that this probably happened because the test didn't call the function, instead they used the assert keyword. Is my guess correct?
No, you gave a single argument of type list. If you have
a = [1,2,3,4]
You have a list
Calling
f(a)
And f([1, 2,3,4]) is the same
Notice the [ brackets.
If, however, you were too call f(1, 2,3,4), that would be a mistake.
Also: the assert keyword still calls the function. It has no way of not calling it, as it has been put in as an expression.
if you call f(g(5))
Then f is already called with the result of g, not the function call itself.
You passed in a list of numbers, which is one argument:
func([1, 3, 3, 5, 5])
vs.
func(1, 3, 3, 5, 5)
It looks like they only did pass a single parameter to your function.
Test.assert_equals(func([1, 3, 3, 5, 5]), [1, 3, 5]))
The array [1, 3, 3, 5, 5] is passed as the single argument to func(), then the array [1, 3, 5] is passed as the second argument to assert_equals().
I was wondering if map can be used at all to sum the elements of a list.
assume a = [1, 2, 3, 4]
list(map(sum, a)) will give an error that int object is not iterable because list wants iterables.
map(sum, a) is a valid statement but given the object, I do not see an easy way to dereference it.
[map(sum, a)] will return an object inside the list
this answer states that it should be easy. What am I missing here?
map applies a function to every element in the list. Instead, you can use reduce:
a = [1, 2, 3, 4]
sum_a = reduce(lambda x, y:x+y, a)
In this case, purely sum can be used, however, to be more functional, reduce is a better option.
Or, in Python3:
from functools import reduce
a = [1, 2, 3, 4]
sum_a = reduce(lambda x, y:x+y, a)
x = list(map(sum,a))
Is equivalent to
x = []
for i in a:
x.append(sum(i))
Sum needs a iterable to apply sum across. If you see the docs syntax goes this way sum(iterable[, start]). Since int is not an iterable you get that error.
Of course if one just want to sum the elements of a list, he should simply call sum(list_).
Now, comming to your question: map, both the Python built-in and the pattern refer to applying a function to a data sequence, and yield another sequence, with a separate result for each element in the initial sequence.
sum does not do that - it yields a single result for the whole sequence. That pattern is called reduce, and so is the Python ex-built-in to do that. In Python 3, it was "demoted" to the functools module, as it is rarely used when compared to the map pattern.
The sum built-in itself employs the "reduce" pattern alone - but if you were to explicitly recreate sum using the reduce pattern it goes like:
from functools import reduce
a = [1, 2, 3, 4]
reduce(lambda result, value: result + value, a, 0)
The first parameter is a callable that takes the "accumulated result so far", the second value is the sequence of items you want to run reduce at, and the third parameter is the initial value to be passed as the accumulated result. (so,it starts at zero). For a multiplicatory, we could use:
reduce(lambda result, value: result * value, a, 1)
update: Python 3.8 implemented the "multiplicatory" in the standard library as math.prod.
The error int object is not iterable is not because list expects an iterable, but sum expected an iterable.
The following code:
map(sum , [1,2,3,4])
Is somewhat equivalent to:
[sum(x) for x in [1,2,3,4]]
Executing the last expression yields the same error.
reduce(lambda x,y:x+y, L) #summing all elements of a list L
Using map reduce and printing the elapsed time in seconds
import time
from six.moves import reduce
import numpy as np
start=time.time()
L = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
result = reduce(lambda x,y:x+y, L)
end=time.time()
print("sum of list L ", L, " is equal to", result)
print("elapsed time is ", end-start, ' seconds')
output:
sum of list L [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] is equal to 55
elapsed time is 0.00014519691467285156 seconds
using python's build-in sum function and elapsed time
start=time.time()
s = sum([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
end=time.time()
print("elapsed time is ", end-start, ' seconds')
output:
elapsed time is 9.226799011230469e-05 seconds
sum is a slightly faster method since 9e-05 is less than 1e-04
Here's one way to do it purely functionally.
from operator import add
from functools import reduce
result = reduce(add, a)
Indirectly you can add all the elements of a list using a map function using a global variable like below:
# reading the file
with open('numbers.txt') as f:
lines = [line.strip() for line in f]
numbers = [int(line) for line in lines]
all_sum = 0
def add(n):
global all_sum
all_sum += n
return(all_sum)
result = map(add, numbers)
print(list(result)[-1])
There is only one number in one line in the text file.
I have a file where I want to extract columns 2, 3, 4, 5 and column -4. These columns are not adjacent.
For reasons of code neatness I'd like to do something like
values = line.split()[columns_to_extract]
instead of
values_part_one = line.split()[columns_to_extract_one]
values_part_two = line.split()[columns_to_extract_two]
Therefore I'd like to make a slice that contains the positions 2, 3, 4, 5 and -4 to be able to extract the values in one line. Is this possible?
If not, are there any other neat oneliners that could do this?
Is it possible to make a slice to do that? No.
However, all is not lost! You can use operator.itemgetter:
getter = operator.itemgetter(2, 3, 4, 5, -4)
example:
>>> import operator
>>> getter = operator.itemgetter(2, 3, 4, 5, -4)
>>> getter(range(50)) # Note, returns a `tuple`
(2, 3, 4, 5, 46)
parts = line.split()
values_part_one = [parts[i] for i in columns_to_extract_one]
values_part_two = [parts[i] for i in columns_to_extract_two]
or, as #mgilson points out, you could use operator.itemgetter to get tuples:
import operator
extract_one = operator.itemgetter(*columns_to_extract_one) # or list explicitly
extract_two = operator.itemgetter(*columns_to_extract_two) # if using fixed cols
parts = line.split()
values_part_one = extract_one(parts)
values_part_Two = extract_two(parts)
Note that both of these will fail with IndexError if the thing you are trying to grab from isn't large enough to contain all of the specified indices.
I'm trying to get the information from a slice. Here's the start of my function. (I have tried "elif isinstance(key, slice):" for the fourth line and can't get that to work)
def __getitem__(self, key):
if isinstance(key,(int, long)):
#do stuff if an int
elif #item is slice
#do stuff if a slice
If I make a function call of obj[4:6] to call this function and I print the "key" variable in the function, it prints "slice(4,6, None)"
How do I parse the 4 and 6 values? What I"m trying to do is be able to use the data from the list inside the function.
>>> slice(4,5).start
4
>>> slice(4,5).stop
5
>>> slice(4,5).step #None
One particularly useful method of the slice object is the indices method:
>>> slice(4,5).indices(12)
(4, 5, 1)
You might use it like this:
for i in range(*my_slice.indices(len(self))):
print self[i]
Note that this really shines with negative indices or steps:
>>> slice(4,-5).indices(12)
(4, 7, 1)
>>> print range(*slice(None,None,-1).indices(12))
[11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
If you want the info from the slice object, access its attributes start, stop, and step. These attributes are documented here.
What I mean is,
I'm looking for really short code that returns the lower value.
for example:
a=[1,2,3,4,5,6,7,8,9,10]
b=[1,2,3,4,5,6,7,8]
len(a) = 10
len(b) = 8
if (fill-this-in):
print(lesser-value)
And I forgot to add that if b is lower than a, I want b returned - not len(b) - the variable b.
print(min(a, b))
You're not hugely clear about what you want, so some alternatives. Given the following two lists:
a = [1,2,3,4,5,6,7,8,9,10]
b = [1,2,3,4,5,6,7,8]
To print the shortest list, you can just do..
>>> print(min(a, b))
[1, 2, 3, 4, 5, 6, 7, 8]
To get the shortest length as an number, you can either min the len() of each list, or do len(min()) (both are identical, choose which ever you find most readable)..
>>> print(min( len(a), len(b) ))
# or..
>>> print(len( min(a, b) ))
8
To print the lowest value in either list, you can supply the list as a single argument to min()
>>> a.extend(b) # Appends b to a
>>> print a
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8]
>>> print(min(a))
1
Finally, another possibility, the list that has the lowest values in total:
>>> max( sum(a), sum(b) )
55
To print the actual list with the highest sum(), you could either use the ternary operator, like..
>>> print a if sum(a) > sum(b) else b
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
..although I never really liked (or use) it, instead using the slight longer, regular if/else statements..
>>> if sum(a) > sum(b):
... print a
... else:
... print b
...
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
If the length of the list is what makes it lower (not its values), then you actually want:
min(a, b, key=len)
which is only incidentally equivalent to
min(a, b)
in the given example.
min() should accomplish what you need
print(min(a,b))
It seems this answer may now be out of date. I just had this same question and found this answer, but wasn't getting the results I expected. Turns out Min doesn't automatically return the shorter of the two lists (in 2.7). To get that you have to use the 'key' argument (introduced in 2.5)(emphasis added):
min(iterable[, key]) min(arg1, arg2, *args[, key]) Return the smallest
item in an iterable or the smallest of two or more arguments.
If one positional argument is provided, iterable must be a non-empty
iterable (such as a non-empty string, tuple or list). The smallest
item in the iterable is returned. If two or more positional arguments
are provided, the smallest of the positional arguments is returned.
The optional key argument specifies a one-argument ordering function
like that used for list.sort(). The key argument, if supplied, must be
in keyword form (for example, min(a,b,c,key=func)).
Changed in version 2.5: Added support for the optional key argument
So in this example, although it seems to work (and still would in 2.7), it only does because the list of integers is the same. However if these were two different non-ordered lists then:
min(a,b)
would return the list with the lowest first integer.
To be sure to get the shorter of two lists, use:
min(a,b, key=len)
heads up, min(a, b, key=len) only works in python 2.5 and up I think.
(it's not working on my macbook with python 2.4, but my linux server with 2.5 is fine)
Is the following what you want?
if len(a) < len(b):
print a
else:
print b
Alternatively, if you want to use the ternary operator like #Andrew G. Johnson:
print a if len(a) < len(b) else b
PS. Remember that Python does not use braces for its blocks, and that its ternary operator is different from C-like languages.
I don't know Python but for something like this I'd use a ternary operator.
print(length(a) < length(b) ? length(a) : length(b))
One thing to note about this that if they are equal it will print length(b)