adding class elements to list elements [duplicate] - python

I am trying to understand how __add__ works:
class MyNum:
def __init__(self,num):
self.num=num
def __add__(self,other):
return MyNum(self.num+other.num)
def __str__(self):
return str(self.num)
If I put them in a list
d=[MyNum(i) for i in range(10)]
this works
t=MyNum(0)
for n in d:
t=t+n
print t
But this does not:
print sum(d)
TypeError: unsupported operand type(s) for +: 'int' and 'instance'
What am I doing wrong? How can I get the sum() to work?
My problem is how to use the sum on a list of objects that support the __add__, need to keep it as generic as possible.

You need to define __radd__ as well to get this to work.
__radd__ is reverse add. When Python tries to evaluate x + y it first attempts to call x.__add__(y). If this fails then it falls back to y.__radd__(x).
This allows you to override addition by only touching one class. Consider for example how Python would have to evaluate 0 + x. A call to 0.__add__(x) is attempted but int knows nothing about your class. You can't very well change the __add__ method in int, hence the need for __radd__. I suppose it is a form of dependency inversion.
As Steven pointed out, sum operates in place, but starts from 0. So the very first addition is the only one that would need to use __radd__. As a nice exercise you could check that this was the case!

>>> help(sum)
Help on built-in function sum in module __builtin__:
sum(...)
sum(sequence[, start]) -> value
Returns the sum of a sequence of numbers (NOT strings) plus the value
of parameter 'start' (which defaults to 0). When the sequence is
empty, returns start.
In other words, provide a start value:
sum(d, MyNum(0))
Edit pasted from my below comment:
sum works with a default start value of the integer zero. Your MyNum class as written does not know how to add itself to integers. To solve this you have two options. Either you can provide a start value to sum that has the same type as you class, or you can implement __radd__, which Python calls when adding values of differing types (such as when the first value in d is added to the default start value of zero).

I oppose relaying on sum() with a start point, the loop hole exposed below,
In [51]: x = sum(d, MyNum(2))
In [52]: x.num
Out[52]: 47
Wondering why you got 47 while you are expecting like
…start from 2nd of MyNum() while leaving first and add them till end, so the expected result = 44 (sum(range(2,10))
The truth here is that 2 is not kept as start object/position but instead treated as an addition to the result
sum(range(10)) + 2
oops, link broken !!!!!!
Use radd
Here below the correct code. Also note the below
Python calls __radd__ only when the object on the right side of the + is your class instance
eg: 2 + obj1
#!/usr/bin/env python
class MyNum:
def __init__(self,num):
self.num=num
def __add__(self,other):
return MyNum(self.num+other.num)
def __radd__(self,other):
return MyNum(self.num+other)
def __str__(self):
return str(self.num)
d=[MyNum(i) for i in range(10)]
print sum(d) ## Prints 45
d=[MyNum(i) for i in range(2, 10)]
print sum(d) ## Prints 44
print sum(d,MyNum(2)) ## Prints 46 - adding 2 to the last value (44+2)

class MyNum:
def __init__(self,num):
self.num=num
def __add__(self,other):
return self.num += other.num
def __str__(self):
return str(self.num)
one = MyNum(1)
two = MyNum(2)
one + two
print(two.num)

Another option is reduce (functools.reduce in Python 3.x).
from functools import reduce
from operators import add
d=[MyNum(i) for i in range(10)]
my_sum = reduce(add,d)

Related

In python 2.7 can you make the __contains__ super method take 3 arguments?

I am wanting to make the super method contains work a little differently.
I want to make it so that you can give it a two integers. And if the first integer is less than the instance of the class, and the second integer is greater than the instance of the class It will return True. This is my attempt.
class test(object):
def __init__(self, item):
self.item= item
def __contains__(self,other1,other2):
if other1<self.item<other2:
return True
else:
return False
x=test(5)
print 1 in x
I get this error
TypeError: __contains__() takes exactly 3 arguments (2 given)
As you can see I don't even know how I would do 3 arguments for the contains method. But for the sake of example, Id try just checking if 5 is in between 1 and 7.
I know I can do this in a method I can make myself, but I'm wanting to work with the in keyword.
If you want some sort of 3-argument in operator, you'd have to build your own deeply modified Python interpreter, including rewriting the fundamental syntax rules.
You could take a tuple easily enough, though:
class Blah(object):
def __init__(self, val):
self.val = val
def __contains__(self, l):
a, b = l
return a < self.val < b
print (1, 3) in Blah(2)
It doesn't make much sense, but it's doable.

Python why do my nested functions give a Nonetype error?

I'm new to programming.
def start():
x = 4
def addition():
n = 3
def exponential():
z = 2
def multiplication():
l = 2
print(x + n ** z * l)
return multiplication
equals = start()
equals()
why am I getting a "Nonetype" object is not callable error?
You're confusing a bunch of programming concepts:
Don't declare a function whenever you only need a statement
You're confusing function declaration with function call (invocation), and also the nesting is pointless. Declaring nested fn2 inside of fn1 doesn't magically also call fn2 and also transmit its return-value back to fn1. You still have to use an explicit return-statement from each fn.(If you forget that, you're implicitly returning None, which is almost surely not what you want)
For now, just don't ever nest functions at all.
Functions with no arguments are essentially useless, they can't take inputs and compute a result. Figure out what their arguments should be.
Specifically for the code you posted, addition(), multiplication() don't have any return value at all, i.e. None. exponential() returns multiplication, i.e. a function which only returns None. But then, both addition() and start() ignore that anyway, since they don't have a return-statement either, hence they implicitly return None.
Calling start() just gives you None, so you're just assigning equals = None. Not the result of some mathematical expression like you intended.
So:
reduce every unnecessary function to just a statement
declare each of your functions separately (non-nested)
each fn must have args (in this case at least two args, to make any sense)
each fn must have a return statement returning some value
only declaring a function and never calling it means it never gets run.
put an empty line in between function declarations (Then it's obvious if you forgot the return-statement)
Credits goes to #BrenBarn for being first to answer this. But I wanna post the code to make it more clear, and point out to some ways to make it better.
def start():
x = 4
def addition():
n = 3
def exponential():
z = 2
def multiplication():
l = 2
print (x + n ** z * l)
return multiplication()
return exponential()
return addition()
equals = start()
print equals #Output: 22
However, this is not the best way to list different methods. You should learn how to use a class in your python code.
I am going to define a class called "mathOperations". I will define three methods (functions): addition,exponential, multiplication. These functions are reusable.
class mathOperations():
def addition(self,x,y):
return x+y
def exponential(self,x,y):
return x**y
def multiplication(self,x,y):
return x*y
m= mathOperations()
z=2
l=2
x=4
n=3
result= m.addition(x,m.multiplication(m.exponential(n,z),l))
print result #Output:22
You should learn how to make your code reusable, try to google "procedural programming"; "Oriented Object Programming", or check "Learn Python the hard way" book. These are first and most used approach to make your code reusable. Think of it like a generic mathematical function to solve problems.

"subtracting" strings, classes in python

Learning about classes in python. I want the difference between two strings, a sort of subtraction. eg:
a = "abcdef"
b ="abcde"
c = a - b
This would give the output f.
I was looking at this class and I am new to this so would like some clarification on how it works.
class MyStr(str):
def __init__(self, val):
return str.__init__(self, val)
def __sub__(self, other):
if self.count(other) > 0:
return self.replace(other, '', 1)
else:
return self
and this will work in the following way:
>>> a = MyStr('thethethethethe')
>>> b = a - 'the'
>>> a
'thethethethethe'
>>> b
'thethethethe'
>>> b = a - 2 * 'the'
>>> b
'thethethe'
So a string is passed to the class and the constructor is called __init__. This runs the constructor and an object is returned, which contains the value of the string? Then a new subtraction function is created, so that when you use - with the MyStr object it is just defining how subtract works with that class? When sub is called with a string, count is used to check if that string is a substring of the object created. If that is the case, the first occurrence of the passed string is removed. Is this understanding correct?
Edit: basically this class could be reduced to:
class MyStr(str):
def __sub__(self, other):
return self.replace(other, '', 1)
Yes, your understanding is entirely correct.
Python will call a .__sub__() method if present on the left-hand operand; if not, a corresponding .__rsub__() method on the right-hand operand can also hook into the operation.
See emulating numeric types for a list of hooks Python supports for providing more arithmetic operators.
Note that the .count() call is redundant; .replace() will not fail if the other string is not present; the whole function could be simplified to:
def __sub__(self, other):
return self.replace(other, '', 1)
The reverse version would be:
def __rsub__(self, other):
return other.replace(self, '', 1)

Problems in summing two polynomials using python

I'm asked to make a program that calculates the addition of two polynomials of n and m degrees. I made two dictionaries (one for the first polynomial and the other is for the other polynomial) since each one has the coefficients as values and degrees as keys so that I can check whether the keys from both dictionaries are identical, then I can sum their values. But I don't know why I always get an error. My code so far is:
class poly:
def __init__(self, L=[], D=[]):
self.coef=L
self.deg=D
def __add__(self,L2):
if len(self.coef)>len(self.deg):
dec=dict(zip(self.deg,self.coef))
dec[0]=self.coef[-1]
else:
dec=dict(zip(self.deg,self.coef))
Dec1=dec
if len(L2.coef)>len(L2.deg):
dec=dict(zip(L2.deg,L2.coef))
dec[0]=L2.coef[-1]
else:
dec=dict(zip(L2.deg,L2.coef))
Dec2=dec
p=[]
if len(Dec2)>len(Dec1):
for i in Dec2:
if i in Dec1:
s=Dec1[i]+Dec2[i]
p=p+[s]
else:
p=p+p[Dec2[i]]
for x in Dec1:
if x in Dec2:
p=p
else:
p=p+[dec1[x]]
return(poly(p))
if len(Dec2)<len(Dec1):
for x in Dec1:
if x in Dec2:
g=Dec1[x]
p=p+[g]
else:
p=p+[Dec1[x]]
for m in Dec2:
if m in Dec1:
p=p
else:
p=p+[Dec2[m]]
return (poly(p))
This code doesn't work for all my examples such as
>>> p=poly([2,4,7,34],[6,4,2])
>>> p1=poly([6,3,7,2,8],[8,4,2,1])
>>> p2=p+p1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
p2=p+p1
File "poly.py", line 31, in __add__
p=p+p[Dec2[i]]
IndexError: list index out of range
>>> #The numbers in the first list is the coefficients and the second list is for degrees
This doesn't work! But it worked when I've done the addition without using class method. I'm a beginner and I did my best to fix the problem.
Another question is how to write the def str for my code? I really don't have any idea what I should write in the beginning. I'm sorry guys but I'm new in programming and I need an easy code such as mine.
By common convention, class names should be capitalized (ie Poly)
You have __add__ doing a lot of stuff that has nothing to do with adding. This should be a warning sign.
A lot of __add__'s work is mucking about with the data storage format. Maybe you should use a better storage format, one which won't need so much reshuffling?
You have a lot of repetitive chunks of code in __add__; this is usually an indicator that the code should be factored into a subroutine.
You have this object (self) making changes to the internal details of another object (L2) - another bad smell.
If you move the normalization code for self (if len(self.coef) > len(self.deg) ...) from __add__ into __init__, this will solve #2, #3, half of #4, and #5 all in one go (you no longer have to "do to" L2, it will "do to" itself).
If you realize that it's pretty much irrelevant whether len(Dec1) > len(Dec2) or not, you can get rid of another block of redundant code. This fixes the other half of #4. Suddenly __add__ shrinks from 48 lines of code to about 12, and becomes much easier to understand and debug.
For sake of comparison:
from itertools import izip_longest, chain, product
from collections import defaultdict
class Poly(object):
def __init__(self, coeff=None, power=None):
if coeff is None: coeff = []
if power is None: power = []
self.d = defaultdict(int)
for c,p in izip_longest(coeff, power, fillvalue=0):
if c != 0:
self.d[p] += c
#classmethod
def fromDict(cls, d):
return cls(d.itervalues(), d.iterkeys())
#property
def degree(self):
return max(p for p,c in self.d.iteritems() if c != 0)
def __add__(self, poly):
return Poly(
chain(self.d.itervalues(), poly.d.itervalues()),
chain(self.d.iterkeys(), poly.d.iterkeys())
)
def __mul__(self, poly):
return Poly(
(cs*cp for cs,cp in product(self.d.itervalues(), poly.d.itervalues())),
(ps+pp for ps,pp in product(self.d.iterkeys(), poly.d.iterkeys()))
)
def __call__(self, x):
return sum(c*x**p for p,c in self.d.iteritems())
def __str__(self):
clauses = sorted(((p,c) for p,c in self.d.iteritems() if c != 0), reverse=True)
return " + ".join("{}x^{}".format(c,p) for p,c in clauses) or "0"
Note that:
Each method is short and does only things relevant to what it is supposed to accomplish.
I purposefully wrote __init__ to be very fault-tolerant; it will cheerfully accept multiple coefficients of a given power and sum them. This allowed me to greatly simplify __add__ and __mul__, basically just throwing all the resulting clauses at a new Poly and letting it clean them up again.
I have included a minimal implementation of __str__, which will result in moderately ugly output like 5x^2 + -2x^1 + -5x^0. You may wish to add special handling for negative coefficients and powers of 1 or 0, to make it produce 5x^2 - 2x - 5 instead.
This is for the purpose of understanding, not plagiarism; do not submit it to your teacher as is, he will never in a million years believe you actually wrote it ;-)

TypeError after overriding the __add__ method

I am trying to understand how __add__ works:
class MyNum:
def __init__(self,num):
self.num=num
def __add__(self,other):
return MyNum(self.num+other.num)
def __str__(self):
return str(self.num)
If I put them in a list
d=[MyNum(i) for i in range(10)]
this works
t=MyNum(0)
for n in d:
t=t+n
print t
But this does not:
print sum(d)
TypeError: unsupported operand type(s) for +: 'int' and 'instance'
What am I doing wrong? How can I get the sum() to work?
My problem is how to use the sum on a list of objects that support the __add__, need to keep it as generic as possible.
You need to define __radd__ as well to get this to work.
__radd__ is reverse add. When Python tries to evaluate x + y it first attempts to call x.__add__(y). If this fails then it falls back to y.__radd__(x).
This allows you to override addition by only touching one class. Consider for example how Python would have to evaluate 0 + x. A call to 0.__add__(x) is attempted but int knows nothing about your class. You can't very well change the __add__ method in int, hence the need for __radd__. I suppose it is a form of dependency inversion.
As Steven pointed out, sum operates in place, but starts from 0. So the very first addition is the only one that would need to use __radd__. As a nice exercise you could check that this was the case!
>>> help(sum)
Help on built-in function sum in module __builtin__:
sum(...)
sum(sequence[, start]) -> value
Returns the sum of a sequence of numbers (NOT strings) plus the value
of parameter 'start' (which defaults to 0). When the sequence is
empty, returns start.
In other words, provide a start value:
sum(d, MyNum(0))
Edit pasted from my below comment:
sum works with a default start value of the integer zero. Your MyNum class as written does not know how to add itself to integers. To solve this you have two options. Either you can provide a start value to sum that has the same type as you class, or you can implement __radd__, which Python calls when adding values of differing types (such as when the first value in d is added to the default start value of zero).
I oppose relaying on sum() with a start point, the loop hole exposed below,
In [51]: x = sum(d, MyNum(2))
In [52]: x.num
Out[52]: 47
Wondering why you got 47 while you are expecting like
…start from 2nd of MyNum() while leaving first and add them till end, so the expected result = 44 (sum(range(2,10))
The truth here is that 2 is not kept as start object/position but instead treated as an addition to the result
sum(range(10)) + 2
oops, link broken !!!!!!
Use radd
Here below the correct code. Also note the below
Python calls __radd__ only when the object on the right side of the + is your class instance
eg: 2 + obj1
#!/usr/bin/env python
class MyNum:
def __init__(self,num):
self.num=num
def __add__(self,other):
return MyNum(self.num+other.num)
def __radd__(self,other):
return MyNum(self.num+other)
def __str__(self):
return str(self.num)
d=[MyNum(i) for i in range(10)]
print sum(d) ## Prints 45
d=[MyNum(i) for i in range(2, 10)]
print sum(d) ## Prints 44
print sum(d,MyNum(2)) ## Prints 46 - adding 2 to the last value (44+2)
class MyNum:
def __init__(self,num):
self.num=num
def __add__(self,other):
return self.num += other.num
def __str__(self):
return str(self.num)
one = MyNum(1)
two = MyNum(2)
one + two
print(two.num)
Another option is reduce (functools.reduce in Python 3.x).
from functools import reduce
from operators import add
d=[MyNum(i) for i in range(10)]
my_sum = reduce(add,d)

Categories

Resources