I am trying to print out a succession of coefficients of a simple polynomial using *args
I roughly understand how *args works and I know how to use a simple loop to print out each coefficient, but in a __repr__ function that is supposed to return only one thing as the return value of function, I am confused how to do this...
class Polynomial:
def __init__(self, *coefficients):
self.coeffs = coefficients
def __repr__(self):
return "Polynomial( {} )".format(self.coeffs)
p1 = Polynomial(1, 2, 3)
p2 = Polynomial(3, 4, 3)
print(p1) # Polynomial( (1, 2, 3) )
print(p2) # Polynomial( (3, 4, 3) )
The outcome of print is what comes after the comments obviously, though what I am after is this format:
1x^2 + 2x + 3
3x^2 + 4x + 3
I have tried the following, but I cannot seem to get it right.
def __repr__(self):
# return "Polynomial( {}x^2 + {}x + {} )".format(self.coeffs)
# return "Polynomial( {0}x^2 + {1}x + {2} )".format(self.coeffs)
# return "Polynomial( {0}x^2 + {1}x + {2} )".format( enumerate(self.coeffs) )
Is there a neat way of doing this without having to loop through the args elements and all in one go within the return statement?
You can use the * syntax in __repr__ as well:
def __repr__(self):
return "Polynomial( {}x^2 + {}x + {} )".format(*self.coeffs)
However, if you want to have __repr__ adjust to the degree of the polynomial, you probably need a loop of some sort:
def __repr__(self):
degree = len(self.coeffs) - 1
polystr = ' + '.join('{}x^{}'.format(term, degree-i)
for i, term in enumerate(self.coeffs))
return "Polynomial( {} )".format(polystr)
Try this (I'm taking the coefficients as a list):
exp = len(self.coeffs)
i = 0
s = ""
while exp > -1:
s += str(coeffs[i]) + "x^" + str(exp) + " "
exp -= -1
i += 1
return s
Or, if you'll always have 2 as the highest exponent, you could use * to select all of the coeffs, as in .format(*self.coeffs).
Related
When framing if condition for 'add_length' , isinstance has 2nd parameter 'length' but in the case of 'add_inches' the 2nd parameter is given int(integer) . I could not understand this part .why can't they both be int(integer). Help is appreciated !
class Length:
def __init__(self, feet, inches):
self.feet = feet
self.inches = inches
def __str__(self):
return f'{self.feet} {self.inches}'
def __add__(self, other):
if isinstance(other, Length):
return self.add_length(other)
if isinstance(other, int):
return self.add_inches(other)
else:
return NotImplemented
def __radd__(self, other):
return self.__add__(other)
def add_length(self, L):
f = self.feet + L.feet
i = self.inches + L.inches
if i >= 12:
i = i - 12
f += 1
return Length(f, i)
def add_inches(self, inches):
f = self.feet + inches // 12
i = self.inches + inches % 12
if i >= 12:
i = i - 12
f += 1
return Length(f, i)
length1 = Length(2, 10)
length2 = Length(3, 5)
print(length1 + length2)
print(length1 + 2`
print(length1 + 20)
print(20 + length1)
The first condition is checking for the scenario when two length objects are added together:
length1 = Length(2, 10)
length2 = Length(3, 5)
print(length1 + length2)
The second condition is checking for the scenario when an integer is added to a length.
print(length1 + 2)
print(length1 + 20)
The conditional calls two different methods add_length and add_inches because of how the data needs to be accessed. That is, for add_length it accessed via properties of the object (L). For add_inches it is accessed from the integer (inches).
From my understanding, this is simply the way this class is designed.
You can either add two objects from the length class together (this is the first isinstance) or you can add an integer in inches to the length (this is the second isinstance).
I tried :
def fibonnaci(n):
total_call = 0
if n ==0 or n == 1:
return 1
else:
if n== 2 or n == 1:
total_call +=0
else:
total_call +=2
return fibonnaci(n - 1) + fibonnaci(n - 2), total_call
n = 8
print(fibonnaci(n))
but I got a error:
TypeError: can only concatenate tuple (not "int") to tuple
How to display the number of calls for fibonnaci?
In your return statement the result of both fibonnaci(n - 1) and fibonnaci(n - 2) could be a tuple (argument > 1), or a single integer (argument <= 1) thus + means concatenation when the first one is a tuple. But when n == 3 in return fibonacci(n - 1) + fibonacci(n - 2), total_call fibonacci(2) is a tuple ((2, total_call)), while fibonacci(1) is an integer (1). So you want to concatenate a tuple with with an integer, which is impossible.
Using Decorators
Using Function Attributes
Reference
Code
def call_counter(func):
" Does the call count for any function "
def helper(x):
helper.calls += 1
return func(x)
helper.calls = 0
return helper
#call_counter
def fib(n):
if n ==0 or n == 1:
return 1
return fib(n - 1) + fib(n - 2)
Usage
fib(5)
print(fib.calls)
fib(10)
print(fib.calls) # Keeps running total so will be from previous
# fib(5) plus current fib(10)
# To reset counter
fib.calls = 0
Using Class
Reference
Code
class countCalls(object):
"""Decorator that keeps track of the number of times a function is called.
::
>>> #countCalls
... def foo():
... return "spam"
...
>>> for _ in range(10)
... foo()
...
>>> foo.count()
10
>>> countCalls.counts()
{'foo': 10}
Found in the Pythod Decorator Library from http://wiki.python.org/moin web site.
"""
instances = {}
def __init__(self, func):
self.func = func
self.numcalls = 0
countCalls.instances[func] = self
def __call__(self, *args, **kwargs):
self.numcalls += 1
return self.func(*args, **kwargs)
def count(self):
"Return the number of times this function was called."
return countCalls.instances[self.func].numcalls
#staticmethod
def counts():
"Return a dict of {function: # of calls} for all registered functions."
return dict([(func.__name__, countCalls.instances[func].numcalls) for func in countCalls.instances])
#countCalls
def fib(n):
if n ==0 or n == 1:
return 1
return fib(n - 1) + fib(n - 2)
Example
print(fib(3)) # Output 3
print(fib.count()) # Output 5
Advantage
Allows obtaining counts of all registered functions (i.e. registered by using decorator)
#countCalls
def f(n):
pass # dummy function
#countCalls
def g(n):
pass # dummy function
for i in range(5):
f(i)
for i in range(10):
g(i)
print(countCalls.counts())
# Outputs: {'f': 5, 'g': 10}
def fib(n):
if n <= 1:
return n, 1
fib_one = fib(n - 1)
fib_two = fib(n - 2)
#Return the result and the number of function calls (+ 1 for the current call)
return fib_one[0] + fib_two[0], fib_one[1] + fib_two[1] + 1
if __name__ == '__main__':
number_of_function_calls = fib(4)[1]
Fib(4) should return 9, which it does
fib(4)
fib(3) fib(2)
fib(2) fib(1) fib(1) fib(0)
fib(1) fib(0)
The problem is "obvious", if you bother to trace the values you're using:
return fibonnaci(n - 1) + fibonnaci(n - 2), total_call
When n is 3, this tries to "add" fibonnaci(2), a tuple, and fibonnaci(1), the integer 1. This is not a legal operation. You need to regularize your return values. You can't magically return the value alone (not the count) when that's what you want; you have to explicitly program the difference: dismember the tuple and add the component values.
Start with your base case being
return 1, 0
Your recursion case needs to add the components. Implementation is left s an exercise for the student.
Here's another answer using a decorator. The advantage of using a decorator is that the base function fib does not need to change. That means the total_count code and multiple return values can be discarded from your original attempt -
#counter(model = fib_result)
def fib(n = 0):
if n < 2:
return n
else:
return fib(n - 1) + fib(n - 2)
Our decorator counter accepts a model so we can respond to the behavior of the decorated function. Our decorated fib will return a fib_result such as { result: ?, count: ? }. That means we also need to handle fib(?) + fib(?) which is why we also defined __add__ -
class fib_result:
def __init__(self, result, count = 0):
if isinstance(result, fib_result):
self.result = result.result
self.count = result.count + count
else:
self.result = result
self.count = count
def __add__(a, b):
return fib_result(a.result + b.result, a.count + b.count)
As you can see, this fib_result is specific to counting fib calls. A different model may be required for counting other recursive functions that return other types of results.
All that's left now is to define our generic counter decorator. Our decorator takes arguments and returns a new decorator, lambda f: ..., which simply captures the passed arguments, lambda *args: ..., and constructs a new model with the result of f(*args) and a base count of 1 -
def counter(model = tuple):
return lambda f: lambda *args: model(f(*args), 1)
Here's how the complete program works -
r = fib(4)
print(f"answer: {r.result}, recurs: {r.count}")
# answer: 3, recurs: 9
r = fib(10)
print(f"answer: {r.result}, recurs: {r.count}")
# answer: 55, recurs: 177
I am currently working on creating a Polynomial class that includes add , mul and eval methods. I'm currently stuck on the addition portion, if someone could give me some help on how to get that figured out that would be greatly appreciated. Everything currently works without errors but when I do p3 = p1 + p2 and I print p3 I get back the two lists together. Any feedback would be greatly appreciated.
class Polynomial(object):
def __init__(self, *coeffs, num = 0):
self.coeffs = list(coeffs) # turned into a list
assert (type(num) is type(1))
self.num = num
# Needs to be operator overload
'''
def __mul__(self, other):
'''
def __eval__(self, other, coeff, x):
result = coeff[-1]
for i in range(-2, -len(coeff)-1, -1):
result = result * x + coeff[i]
return result
def __add__(self, other):
assert type(other) is Polynomial
num = self.coeffs + other.coeffs
return Polynomial(num)
def __sub__(self, other):
assert type(other) is Polynomial
num = self.coeffs - other.coeffs
return Polynomial(num)
def __represntation__(self):
return "Polynomial" + str(self.coeffs)
def __str__(self):
rep = ""
degree = len(self.coeffs) - 1
rep += str(self.coeffs[0]) + "x^" + str(degree)
for i in range(1, len(self.coeffs)-1):
coeff = self.coeffs[i]
if coeff < 0:
rep += " - " + str(-coeff) + "x^" + str(degree - i)
else:
rep += " + " + str(coeff) + "x^" + str(degree - i)
if self.coeffs[-1] < 0:
rep += " - " + str(-self.coeffs[-1])
else:
rep += " + " + str(self.coeffs[-1])
return rep
You cannot directly add two lists.
def __add__(self, other):
assert type(other) is Polynomial
assert len(self.coeffs) != len(other.coeffs)
new_ceffs = [item1 + item2 for (item1, item2) in zip(self.coeffs, other.coeffs)]
return Polynomial(new_ceffs)
The problem is here:
num = self.coeffs + other.coeffs
Adding one list to another list concatenates them. To simply add corresponding elements to each other, you'd want to do
from itertools import zip_longest
...
num = [a + b for (a, b) in zip_longest(self.coeffs, other.coeffs, fillvalue=0)]
We use zip_longest() instead of the more generic zip() because one polynomial is possibly longer than the other and we don't want to ruin it. Either one of them will group together the corresponding elements so that we can easily add them and make a list of those.
You would do something similar for subtraction.
you should reverse the order of coefficients passed to your constructor so that indexes in the self.coeffs list correspond to the exponents. This would simplify the rest of your code and allow you to use zip_longest for additions and subtractions.
When you get to other operations however, I think you will realize that your internal structure would be easier to manage if it was a dictionary. A dictionary is more permissive of missing entries thus avoiding preoccupations of allocating spaces for new indexes.
class Polynomial(object):
def __init__(self, *coeffs):
self.coeffs = {exp:c for exp,c in enumerate(coeffs[::-1])}
def __add__(self, other):
assert type(other) is Polynomial
result = Polynomial(0)
result.coeffs = {**self.coeffs}
for exp,c in other.coeffs.items():
result.coeffs[exp] = result.coeffs.get(exp,0) + c
return result
def __sub__(self, other):
assert type(other) is Polynomial
result = Polynomial(0)
result.coeffs = {**self.coeffs}
for exp,c in other.coeffs.items():
result.coeffs[exp] = result.coeffs.get(exp,0) - c
return result
def __mul__(self, other):
assert type(other) is Polynomial
result = Polynomial(0)
for exp1,c1 in self.coeffs.items():
for exp2,c2 in other.coeffs.items():
result.coeffs[exp1+exp2] = result.coeffs.get(exp1+exp2,0) + c1*c2
return result
def __representation__(self):
return "Polynomial" + str(self.coeffs)
def __str__(self):
result = [""]+[f"{c}x^{i}" for i,c in sorted(self.coeffs.items()) if c]+[""]
result = "+".join(reversed(result))
result = result.replace("+1x","+x")
result = result.replace("-1x","-x")
result = result.replace("x^0","")
result = result.replace("x^1+","x+")
result = result.replace("+-","-")
result = result.strip("+")
result = result.replace("+"," + ")
result = result[:1]+result[1:].replace("-"," - ")
return result.strip()
I am developing a module that draws lines and finds their midpoints. For purposes of testing, I want to create some string outputs from the relevant classes.
class Line:
def __init__(self, endpoints):
self.start = endpoints[0]
self.end = endpoints[1]
def midpoint():
x = (start.getX + end.getX) / 2.0
y = (start.getY + end.getY) / 2.0
return Point(x, y)
def __str__(self):
return "line from " + `self.start` + " to " + `self.end` + "."
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def getX():
return x
def getY():
return y
def __str__(self):
return "[" + str(self.x) + ", " + str(self.y) + "]"
__repr__ = __str__
point1 = Point(4,5)
point2 = Point(0,0)
line1 = Line([point1, point2])
print line1
print line1.midpoint
Expected output:
line from [4, 5] to [0, 0]
[2.0, 2.5]
Instead I get:
line from [4, 5] to [0, 0]
<bound method Line.midpoint of <__main__.Line instance of 0x105064e18>>
How can I get the expected string representation of the midpoint, which is being returned as an instance of the Point class?
You are printing the method itself, not the returned value of the method. Change your last line to this:
print line1.midpoint()
Also, the first definition line of your method should use self as the only parameter, like so:
def midpoint(self):
The same applies to the rest of the methods, they should have self as a parameter (Point.getX and Point.getY).
In the midpoint method, you should have start.getX(), start.getY(), end.getX(), and end.getY() should have "self." in front of it. You should also have "self" as a parameter for the method for every method in a class.
midpoint method
I will paste the entire code below to show you exactly what I have done.
entire code
I'm trying to use the parent class's __div__() in order to maintain the same type so that many operations can be called at once as in the last example mix1 = bf2/bf4*bf1%bf5 in main() below where multiple arithmetic operations are strung together. For some reason, I can use super() in __add__() but not in __div__(). The error is "IndexError: list index out of range" and I've been going over and over this without any progress. Note that this is all related to polynomial arithmetic within a finite field.
I'm including the parsePolyVariable() and it's dependents (sorry if it looks like there's a bit of code but I assure you it's all for a good cause and builds character), since that's where the list error seems to be stemming from but I can't for the life of me figure out where everything is going very wrong. I'm teaching myself Python, so I'm sure there are some other beginners out there who will see where I'm missing the obvious.
I've been looking over these but they don't seem to be related to this situation:
http://docs.python.org/2/library/functions.html#super
Python super(Class, self).method vs super(Parent, self).method
How can I use Python's super() to update a parent value?
import re
class GF2Polynomial(object): #classes should generally inherit from object
def __init__(self, string):
'''__init__ is a standard special method used to initialize objects.
Here __init__ will initialize a gf2infix object based on a string.'''
self.string = string #basically the initial string (polynomial)
#if self.parsePolyVariable(string) == "0": self.key,self.lst = "0",[0]
#else:
self.key,self.lst = self.parsePolyVariable(string) # key determines polynomial compatibility
self.bin = self.prepBinary(string) #main value used in operations
def id(self,lst):
"""returns modulus 2 (1,0,0,1,1,....) for input lists"""
return [int(lst[i])%2 for i in range(len(lst))]
def listToInt(self,lst):
"""converts list to integer for later use"""
result = self.id(lst)
return int(''.join(map(str,result)))
def parsePolyToListInput(self,poly):
"""
replaced by parsePolyVariable. still functional but not needed.
performs regex on raw string and converts to list
"""
c = [int(i.group(0)) for i in re.finditer(r'\d+', poly)]
return [1 if x in c else 0 for x in xrange(max(c), -1, -1)]
def parsePolyVariable(self,poly):
"""
performs regex on raw string, converts to list.
also determines key (main variable used) in each polynomial on intake
"""
c = [int(m.group(0)) for m in re.finditer(r'\d+', poly)] #re.finditer returns an iterator
if sum(c) == 0: return "0",[0]
letter = [str(m.group(0)) for m in re.finditer(r'[a-z]', poly)]
degree = max(c); varmatch = True; key = letter[0]
for i in range(len(letter)):
if letter[i] != key: varmatch = False
else: varmatch = True
if varmatch == False: return "error: not all variables in %s are the same"%a
lst = [1 if x in c else (1 if x==0 else (1 if x=='x' else 0)) for x in xrange(degree, -1, -1)]
return key,lst
def polyVariableCheck(self,other):
return self.key == other.key
def prepBinary(self,poly):
"""converts to base 2; bina,binb are binary values like 110100101100....."""
x = self.lst; a = self.listToInt(x)
return int(str(a),2)
def __add__(self,other):
"""
__add__ is another special method, and is used to override the + operator. This will only
work for instances of gf2pim and its subclasses.
self,other are gf2infix instances; returns GF(2) polynomial in string format
"""
if self.polyVariableCheck(other) == False:
return "error: variables of %s and %s do not match"%(self.string,other.string)
return GF2Polynomial(self.outFormat(self.bin^other.bin))
def __sub__(self,other):
"""
__sub__ is the special method for overriding the - operator
same as addition in GF(2)
"""
return self.__add__(other)
def __mul__(self,other):
"""
__mul__ is the special method for overriding the * operator
returns product of 2 polynomials in gf2; self,other are values 10110011...
"""
if self.polyVariableCheck(other) == False:
return "error: variables of %s and %s do not match"%(self.string,other.string)
bitsa = reversed("{0:b}".format(self.bin))
g = [(other.bin<<i)*int(bit) for i,bit in enumerate(bitsa)]
return GF2Polynomial(self.outFormat(reduce(lambda x,y: x^y,g)))
def __div__(self,other):
"""
__div__ is the special method for overriding the / operator
returns quotient formatted as polynomial
"""
if self.polyVariableCheck(other) == False:
return "error: variables of %s and %s do not match"%(self.string,other.string)
if self.bin == other.bin: return 1
return GF2Polynomial(self.outFormat(self.bin/other.bin))
def __mod__(self,other):
"""
__mod__ is the special method for overriding the % operator
returns remainder formatted as polynomial
"""
if self.polyVariableCheck(other) == False:
return "error: variables of %s and %s do not match"%(self.string,other.string)
if self.bin == other.bin: return 0
return GF2Polynomial(self.outFormat(self.bin%other.bin))
def __str__(self):
return self.string
def outFormat(self,raw):
"""process resulting values into polynomial format"""
raw = "{0:b}".format(raw); raw = str(raw[::-1]); g = [] #reverse binary string for enumeration
g = [i for i,c in enumerate(raw) if c == '1']
processed = "x**"+" + x**".join(map(str, g[::-1]))
proc1 = processed.replace("x**1","x"); proc2 = proc1.replace("x**0","1")
if len(g) == 0: return 0 #return 0 if list empty
return proc2 #returns result in gf(2) polynomial form
class BinaryField(GF2Polynomial):
def __init__(self, poly, mod):
if mod == "0": self.string = "Error: modulus division by 0"
elif mod == "0": self.string = "%s is 0 so resulting mod is 0"%(poly)
fieldPoly = GF2Polynomial(poly) % mod
if fieldPoly == 0: self.string = "%s and %s are the same so resulting mod is 0"%(poly,mod)
else: super(BinaryField, self).__init__(fieldPoly.string)
#self.degree = len(str(fieldPoly))
def polyFieldCheck(self,other):
return self.degree() == other.degree()
def __add__(self, other):
"""
inherited from GF2Polynomial
"""
return super(BinaryField, self).__add__(other) % min(other,self)
def __sub__(self,other):
"""
inherited from GF2Polynomial
"""
return self.__add__(other)
def __mul__(self, other):
"""
special method of BinaryField, needed for format adjustments between classes
"""
#print "self = %s,%s other = %s,%s "%(self.degree(),type(self.degree()),other.degree(),type(other.degree()))
if self.polyVariableCheck(other) == False:
return "error: variables of %s and %s do not match"%(self.string,other.string)
if self.polyFieldCheck(other) == False:
return "error: fields of %s and %s do not match"%(self.string,other.string)
else: print "Operation will proceed: fields of %s and %s match"%(self.string,other.string)
bitsa = reversed("{0:b}".format(self.bin))
g = [(other.bin<<i)*int(bit) for i,bit in enumerate(bitsa)]
result = reduce(lambda x,y: x^y,g)%min(self.bin,other.bin)
return GF2Polynomial(self.outFormat(result))
def __div__(self, other):
"""
special method of BinaryField, needed for format adjustments between classes
"""
if self.polyVariableCheck(other) == False:
return "error: variables of %s and %s do not match"%(self.string,other.string)
if self.polyFieldCheck(other) == False:
return "error: fields of %s and %s do not match"%(self.string,other.string)
else: print "Operation will proceed: fields of %s and %s match"%(self.string,other.string)
if self.bin == other.bin: return 1
result = self.bin/other.bin
#return self.outFormat(result)
return super(BinaryField, self).__div__(other) #% min(other,self)
def degree(self):
return len(self.lst)-1
And here's the main():
if __name__ == '__main__':
## "x**1 + x**0" polynomial string style input
poly1 = "x**14 + x**1 + x**0"; poly2 = "x**6 + x**2 + x**1"; poly3 = "y**6 + y**2 + y**1"
a = GF2Polynomial(poly1); b = GF2Polynomial(poly2); c = GF2Polynomial(poly3)
## "x+1" polynomial string style input
poly4 = "x**14 + x + 1"; poly5 = "x**6 + x**2 + x"; poly6 = "x**8 + x**3 + 1"
d = GF2Polynomial(poly4); e = GF2Polynomial(poly5); f = GF2Polynomial(poly6)
poly7 = "x**9 + x**5 + 1"; poly8 = "x**11 + x**7 + x**4 + 1"; poly9 = "x**5 + x**4 + x**2 + x"
g = GF2Polynomial(poly7); h = GF2Polynomial(poly8); i = GF2Polynomial(poly9)
## g = GF2Polynomial("x**5 + x**4 + x**3 + 1"); h = GF2Polynomial("x**5 + x"); print "(g*h)%b = ",(g*h)%b
## dd = GF2Polynomial("x**0"); print "dd -- ",dd
## ee = GF2Polynomial("0"); print "ee -- ",ee
bf1 = BinaryField(poly1,b); print bf1; print "degree bf1 = ",bf1.degree()
bf2 = BinaryField(poly4,e); print "bf2 ",bf2; bf3 = BinaryField(poly4,d); print "bf3 ",bf3,type(bf3)
bf4 = BinaryField(poly4,h); bf5 = BinaryField(poly9,e); bf6 = BinaryField(poly8,i)
add1 = bf1+bf2
print "add1 ",add1
div1 = bf1/bf2
print "div1 ",div1,type(div1)
mix1 = bf2*bf1%bf5
print "mix1 ",mix1,type(mix1)
EDIT:
The full traceback --
Message File Name Line Position
Traceback
<module> C:\Users\win7pro-vm\Desktop\crypto\GF2BinaryField.py 233
__div__ C:\Users\win7pro-vm\Desktop\crypto\GF2BinaryField.py 197
__div__ C:\Users\win7pro-vm\Desktop\crypto\GF2BinaryField.py 100
__init__ C:\Users\win7pro-vm\Desktop\crypto\GF2BinaryField.py 20
parsePolyVariable C:\Users\win7pro-vm\Desktop\crypto\GF2BinaryField.py 48
IndexError: list index out of range
For reference line 48 is degree = max(c); varmatch = True; key = letter[0].
Personal notes and information were removed, adjusting the line numbers.
Your return GF2Polynomial(self.outFormat(self.bin/other.bin)) line results in the string 1, which is then passed to the GF2Polynomial.parsePolyVariable() method.
This value has no letters, so the line:
letter = [str(m.group(0)) for m in re.finditer(r'[a-z]', poly)]
returns an empty list. The next line:
degree = max(c); varmatch = True; key = letter[0]
then fails because key = letter[0] gives a IndexError exception.
Your code is hard to read because you use one-letter variables and put multiple statements on one line, so it is hard to make out what your expectations are in that function.
The exception has otherwise nothing to do with super(). There is a simple bug in your own code somewhere.