Why sum start value is not zero value of iterable? - python

Why is sum not able to take correct zero value automatically?
>>> sum((['1'], ['2']))
Traceback (most recent call last):
File "<pyshell#13>", line 1, in <module>
sum((['1'], ['2']))
TypeError: unsupported operand type(s) for +: 'int' and 'list'
>>> sum((['1'], ['2']), [])
['1', '2']
It is simple to implement like this:
>>> def sum(s, start=None):
it = iter(s)
n = next(it)
if start is None:
start = type(n)()
return n + __builtins__.sum(it, start)
>>> sum((['1'], ['2']))
['1', '2']
>>>
But sum does not anyway join strings, so maybe it is just to encourage to use proper methods for different 'summings'.
On the other hand if it is meant to be used only for numbers, why not sum_numbers not sum as name to make it clear.
EDIT: to handle empty sequence we must add little code:
>> sum([])
Traceback (most recent call last):
File "<pyshell#36>", line 1, in <module>
sum([])
File "<pyshell#28>", line 3, in sum
n = next(it)
StopIteration
>>> def sum(s, start=None):
it = iter(s)
try:
n= next(it)
except:
return 0
if start is None:
start = type(n)()
return n + __builtins__.sum(it, start)
>>> sum([])
0
>>>

Inferring the zero value is impossible in the general case. What if the iterable produces instances of a user-defined class that has no zero-argument constructor? And as you've shown, it's easy to provide it yourself.

Related

How to "push" a tuple in one line of code?

Why can I not replace the following first code:
def conversion7(x,k):
l=list(x)
l.append(k)
return tuple(l)
t=(1,2,3,4)
print(conversion7(t,7))
With the second one:
def conversion7(x,k):
return tuple(list(x).append(k))
t=(1,2,3,4)
print(conversion7(t,7))
The first code works. Here is the compiler output of the second code:
Traceback (most recent call last):
File "<string>", line 5, in <module>
File "<string>", line 2, in conversion7
TypeError: 'NoneType' object is not iterable
>
The purpose of the codes is to push a tuple by converting it to a list, pushing the list and then converting that back to a tuple.
Since append does not return a reference to the list, as chepner has pointed out, you could do the following:
def conversion7(x,k):
return tuple(list(x) + [k])
Or skip list conversion and immediately add tuples:
def conversion7(x,k):
return x + (k,)

Python all short-circuiting with None element

I read everywhere that Python all and any functions support short-circuiting.
However:
a = None
all((a is not None, a + 1 > 2))
Throws the following error:
Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/IPython/core/interactiveshell.py", line 3331, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-4-6e28870e65c8>", line 1, in <module>
all((a is not None, a + 1 > 2))
TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'
I would have expected the code not to evaluate a + 1 > 2 since a is None.
Why is this happening? Is this because each term is evaluated before the call? Am I forced to use the and operator as in a is not None and a + 1 > 2?
The tuple (a is not None, a + 1 > 2) needs to be created before all() can be called. It is during the creation of the tuple that the TypeError is raised. all() doesn't even get a chance to run.
If you want to see all's short circuiting in action, pass it a generator expression. For example:
>>> all('foobaR'[i].islower() for i in range(7))
False
>>> all('foobar'[i].islower() for i in range(7))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <genexpr>
IndexError: string index out of range
In the first run all stops when it hits the 'R'.islower() case since that's False. In the second run it keeps going until i == 6, which triggers an index error.
Am I forced to use the and operator as in a is not None and a + 1 > 2?
I wouldn't say "forced"—I'm sure there other convoluted options—but yes, that's the obvious and idiomatic way to write it.

generators for argument values

I'm having trouble understanding generators when they are used as arguments. Here are a couple cases where I don't fully understand the source of the errors, or at least how to work around them. Explicit fixes as well as broader explanations on the functionality/use of generators would be appreciated.
(I included 3 examples because I have a feeling they are all just facets of the same misunderstanding. I think the question would be better served as one post rather than 3. Please comment if you disagree.)
In the first example: I understand that generator objects cannot be added explicitly, but how should the script be modified to use (unpack?) the generators?
>>> def f(x): return x
>>> def g(x): yield x
>>> def h(x, y): return x + y
>>> h(f(3), f(4))
7
>>> h(g(3), g(4))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in h
TypeError: unsupported operand type(s) for +: 'generator' and 'generator'
In the second example: I'm having trouble understanding how a predefined generator object could be used to generate argument values for a function.
>>> print(*(x*2 for x in range(3)))
0 2 4
>>> print(x*2 for x in range(3))
<generator object <genexpr> at 0x7fc71f050db0>
>>> print(*(yld(x) for x in range(3)))
<generator object yld at 0x7fc71f050c50> <generator object yld at 0x7fc71f050ca8> <generator object yld at 0x7fc71f050e08>
In this third example, how could a generator be used for a multi-argument function?
def yld(x): yield x
(lambda x, y: x + y)(*(yld(x) for x in range(2)))

TypeError: 'int' object is not callable for a recursive function

a = 3
def f(x):
x = (x**3-4*x)/(3(x**2)-4)
return x
while True:
print(a)
a = f(a)
I'm getting a type error here, and I'm not sure why. I'm trying to run this recursive function, is there any way to fix this?
You need a * operator after your parentheses. Multiplication is only implied in mathematical notation in this context, in Python it looks like you're trying to call a function.
3(x**2)
So it would be
3*(x**2)
For example
>>> 3(5*2)
Traceback (most recent call last):
File "<pyshell#0>", line 1, in <module>
3(5*2)
TypeError: 'int' object is not callable
>>> 3*(5*2)
30

Error while working with MutableList

I'm working on a simple size() method while working with Mutablelists and I keep getting the following error:
>>> xs = MutableList
>>> xs
<class __main__.MutableList at 0x02AC6848>
>>> xs.size()
Traceback (most recent call last):
File "<pyshell#1>", line 1, in <module>
xs
.size()
File "C:\Users\safim\Desktop\Python HW 4\a3_1.py", line 59, in size
for x in self :
TypeError: iteration over non-sequence
The code I used was:
result = 0
for x in self :
result + 1
return result
I appreciate the help in advance.
xs is the same object as MutableList, because you made it so:
xs = MutableList
The message printed even tells you this:
<class __main__.MutableList at 0x02AC6848>
As it says, xs is the class, not an instance of that class.
You can't call MutableList.size() (which is what you're trying to do, because xs and MutableList are the same thing) because that doesn't tell it what instance you want to use.
Did you mean to instantiate a MutableList? If so:
xs = MutableList()
Your other code won't work either, since result + 1 adds 1 to result and then throws away that number (you never assign it to a variable). Most likely you mean result += 1.

Categories

Resources