"class name" object has no attribute "function name" in Recursive Function - python

I am trying to implement a recursive function, in which my base condition is working fine, another condition is also working properly. But when I jump to the recursive condition it gives me error that,"class name" object has no attribute "function name" .
My class:
class NGram(object):
def __init__(self):
self.n = None
self.ngramprobability = None
self.uniquegrams = [
["sample", "this"],
["is", "a"],
["this", "is"],
["sample"],
["this"],
["a"],
["is"],
["a", "sample"],
]
self.out = [
[
["sample", 0.16666666666666666],
["this", 0.3333333333333333],
["a", 0.16666666666666666],
["is", 0.3333333333333333],
],
[
["sample", "this", 1.0],
["is", "a", 0.5],
["this", "is", 1.0],
["a", "sample", 1.0],
],
]
def get_prob(self, words):
if len(words) == 1:
probability = [j[-1] for i in self.out for j in i if j[:-1] == words][0]
return probability # condition works fine
elif words in self.uniquegrams:
probability = [j[-1] for i in self.out for j in i if j[:-1] == words][0]
return probability # condition works fine
else:
return self.get_prob(self, words[1:]) * 0.4
My script that is raising errors:
# train bi_gram
bi_gram = NGram()
c = bi_gram.get_prob(["this", "sample"])
print(c)
I am not able to understand where I am making mistake. Please help me in solving this error.

Since you don't seem to understand what minimal reproducible example means, here are two examples (you'll notice that I removed everything that's irrelevant and only kept what's necessary to reproduce the issues) :
1/ forgetting to use self. to reference the method:
class NGram(object):
def get_prob(self, words):
if len(words) == 1:
return 1
else:
return get_prob(words[1:]) * 0.4
ng = NGram()
print(ng.get_prob(["foo"]))
print(ng.get_prob(["foo", "bar"]))
which constantly raises a NameError on the second call:
1
Traceback (most recent call last):
File "probs.py", line 10, in <module>
print(ng.get_prob(["foo", "bar"]))
File "probs.py", line 6, in get_prob
return get_prob(words[1:]) * 0.4
NameError: global name 'get_prob' is not defined
2/ using self. to reference the method but incorrectly passing self as argument:
class NGram(object):
def get_prob(self, words):
if len(words) == 1:
return 1
else:
return self.get_prob(self, words[1:]) * 0.4
ng = NGram()
print(ng.get_prob(["foo"]))
print(ng.get_prob(["foo", "bar"]))
which constantly raises a TypeError on the second call:
1
Traceback (most recent call last):
File "probs.py", line 10, in <module>
print(ng.get_prob(["foo", "bar"]))
File "probs.py", line 6, in get_prob
return self.get_prob(self, words[1:]) * 0.4
TypeError: get_prob() takes 2 positional arguments but 3 were given
And to address your issue - which you wouldn't have if you had done the tutorial -, the correct way is:
class NGram(object):
def get_prob(self, words):
if len(words) == 1:
return 1
else:
return self.get_prob(words[1:]) * 0.4
ng = NGram()
print(ng.get_prob(["foo"]))
print(ng.get_prob(["foo", "bar"]))
which works as expected:
1
0.4
Now may I kindly suggest you do the full official tutorial, and for future questions do some search before asking here ([which you are supposed to do(https://stackoverflow.com/help/how-to-ask)), and provide all relevant informations and (when relevant) a real minimal and reproducible example ?

Short answer: replace your code at line #22 from
return self.get_prob(self, words[1:]) * 0.4
to
return self.get_prob(words[1:]) * 0.4
You are not supposed to give self as an argument when calling any function of a class (it's only included in the definition).
Long answer: Check #Bruno's answer

Related

Stuck on Python "KeyError: <exception str() failed>" in BFS code of a water jug scenario

Intended Function of code: Takes a user input for the volume of 3 jars(1-9) and output the volumes with one of the jars containing the target length. jars can be Emptied/Filled a jar, or poured from one jar to another until one is empty or full.
With the code I have, i'm stuck on a key exception error .
Target length is 4 for this case
Code:
`
class Graph:
class GraphNode:
def __init__(self, jar1 = 0, jar2 = 0, jar3 = 0, color = "white", pi = None):
self.jar1 = jar1
self.jar2 = jar2
self.jar3 = jar3
self.color = color
self.pi = pi
def __repr__(self):
return str(self)
def __init__(self, jl1 = 0, jl2 = 0, jl3 = 0, target = 0):
self.jl1 = jl1
self.jl2 = jl2
self.jl3 = jl3
self.target = target
self.V = {}
for x in range(jl1 + 1):
for y in range(jl2 + 1):
for z in range(jl3 + 1):
node = Graph.GraphNode(x, y, z, "white", None)
self.V[node] = None
def isFound(self, a: GraphNode) -> bool:
if self.target in [a.jar1, a.jar2, a.jar3]:
return True
return False
pass
def isAdjacent(self, a: GraphNode, b: GraphNode) -> bool:
if self.V[a]==b:
return True
return False
pass
def BFS(self) -> [] :
start = Graph.GraphNode(0, 0, 0, "white")
queue=[]
queue.append(start)
while len(queue)>0:
u=queue.pop(0)
for v in self.V:
if self.isAdjacent(u,v):
if v.color =="white":
v.color == "gray"
v.pi=u
if self.isFound(v):
output=[]
while v.pi is not None:
output.insert(0,v)
v=v.pi
return output
else:
queue.append(v)
u.color="black"
return []
#######################################################
j1 = input("Size of first jar: ")
j2 = input("Size of second jar: ")
j3 = input("Size of third jar: ")
t = input("Size of target: ")
jar1 = int(j1)
jar2 = int(j2)
jar3 = int(j3)
target = int(t)
graph1 = Graph(jar1, jar2, jar3, target)
output = graph1.BFS()
print(output)
`
**Error: **
line 37, in isAdjacent
if self.V[a]==b:
KeyError: <exception str() failed>
Strange but when I first ran this in the IPython interpreter I got a different exception:
... :35, in Graph.isAdjacent(self, a, b)
34 def isAdjacent(self, a: GraphNode, b: GraphNode) -> bool:
---> 35 if self.V[a]==b:
36 return True
37 return False
<class 'str'>: (<class 'RecursionError'>, RecursionError('maximum recursion depth exceeded while getting the str of an object'))
When I run it as a script or in the normal interpreter I do get the same one you had:
... line 35, in isAdjacent
if self.V[a]==b:
KeyError: <exception str() failed>
I'm not sure what this means so I ran the debugger and got this:
File "/Users/.../stackoverflow/bfs1.py", line 1, in <module>
class Graph:
File "/Users/.../stackoverflow/bfs1.py", line 47, in BFS
if self.isAdjacent(u,v):
File "/Users/.../stackoverflow/bfs1.py", line 35, in isAdjacent
if self.V[a]==b:
KeyError: <unprintable KeyError object>
Uncaught exception. Entering post mortem debugging
Running 'cont' or 'step' will restart the program
> /Users/.../stackoverflow/bfs1.py(35)isAdjacent()
-> if self.V[a]==b:
(Pdb) type(a)
<class '__main__.Graph.GraphNode'>
(Pdb) str(a)
*** RecursionError: maximum recursion depth exceeded while calling a Python object
So it does seem like a maximum recursion error. (The error message you originally got is not very helpful). But the words <unprintable KeyError object> are a clue. It looks like it was not able to display the KeyError exception...
The culprit is this line in your class definition:
def __repr__(self):
return str(self)
What were you trying to do here?
The __repr__ function is called when the class is asked to produce a string representation of itself. But yours calls the string function on the instance of the class so it will call itself! So I think you actually generated a second exception while the debugger was trying to display the first!!!.
I replaced these lines with
def __repr__(self):
return f"GraphNode({self.jar1}, {self.jar2}, {self.jar3}, {self.color}, {self.pi})"
and I don't get the exception now:
Size of first jar: 1
Size of second jar: 3
Size of third jar: 6
Size of target: 4
Traceback (most recent call last):
File "/Users/.../stackoverflow/bfs1.py", line 77, in <module>
output = graph1.BFS()
File "/Users/.../stackoverflow/bfs1.py", line 45, in BFS
if self.isAdjacent(u,v):
File "/Users/.../stackoverflow/bfs1.py", line 33, in isAdjacent
if self.V[a]==b:
KeyError: GraphNode(0, 0, 0, white, None)
This exception is easier to interpret. Now it's over to you to figure out why this GraphNode was not found in the keys of self.V!

Not sure why I'm getting this attribute error

The assertEqual tests are from a module that just calls a function, runs some data through it, computes the result of that processed data, and compares it to my predicted answer. For example, my predicted answer for total_test was 6.0.
When I run my code (below being the troubled part), I get this error:
Traceback (most recent call last):
File "C:/Users/anon/Desktop/test.py", line 72, in <module>
TransactionTest().run()
File "C:/Users/anon/Desktop/test.py", line 68, in run
self.total_test()
File "C:/Users/anon/Desktop/test.py", line 60, in total_test
assertEqual(self.__t1.total(), 6.0)
File "C:/Users/anon/Desktop/test.py", line 18, in total
return sum(map(lambda p: p.cost(), self.__purchases))
File "C:/Users/anon/Desktop/test.py", line 18, in <lambda>
return sum(map(lambda p: p.cost(), self.__purchases))
AttributeError: 'float' object has no attribute 'cost'
All the line numbers should be shifted down a few lines to account for me copy and pasting it here very slightly modified.
Essentially, my total_test function is causing a crash when it's called. Not sure why I'm getting an attribute error.
class Transaction:
def __init__(self, purchases, tax_rate):
self.__purchases = purchases
self.__tax_rate = tax_rate
def total(self):
return sum(map(lambda p: p.cost(), self.__purchases))
def tax_rate(self):
return self.__tax_rate
def total_taxable(self):
taxable_items = filter(lambda p: p.item().taxable(),self.__purchases)
return sum(map(lambda p: p.cost(), taxable_items))
def grand_total(self):
return self.total() + self.__tax_rate * self.total_taxable()
def __str__(self):
return "Total: " + self.__total + ";" + "Total_tax: " + self.__total_taxable * self.__tax_rate + ";" + "Grand Total: " + self.__grand_total
def print_receipt(self):
f = open("receipt.txt", "w")
f.write("\n".join(map(lambda p:str(p),self.__purchases)))
f.write("\n")
f.write("Total: $%.2f" % self.total())
f.write("\n")
f.write("Tax ( $%.2f # %.2f %%): $%.2f" %(self.total_taxable(), self.__tax_rate * 100, self.__tax_rate * self.total_taxable()))
f.write("\n")
f.write("Grand Total: $%.2f" % self.grand_total())
f.close()
#problem 9-----------------------------------------------------------------------------------
class TransactionTest:
print('----------------------------')
def __init__(self):
t_list = [1.0,2.0,3.0]
self.__t1 = Transaction(t_list, 0.05)
#self.__d2 = Transaction(3.0, 0.06)
def total_test(self):
print('total_test-----------------------------------------')
assertEqual(self.__t1.total(), 6.0)
def tax_rate_test(self):
print('tax_rate_test--------------------------------------')
assertEqual(self.__t1.tax_rate(), 0.05)
#assertEqual(self.__d2.tax_rate() = Transaction(0.06))
def run(self):
self.total_test()
#self.tax_rate_test()
#self.str_test()
TransactionTest().run()
Your test code passes a list of three float instances, [1.0,2.0,3.0], as the purchases argument to Transaction's initializer. However, the other Transaction methods try to call various methods (e.g. cost() and item()) on the values from that list, and since float instances don't have the methods an exception is raised.
I suspect your Transaction code is intended to be run on a list of some other kind of object, where the appropriate methods are defined. You need to rewrite your tests to use the right kind of objects.

Error: 'str ' object does not support item assignment python

Error: 'str ' object does not support item assignment python
dict=['A', 'a','B', 'b','C', 'c','D', 'd','E', 'e','F', 'f','G', 'g','H', 'h','I', 'i','J', 'j','K', 'k','L', 'l','M', 'm','N', 'n','P', 'o','P', 'p','Q', 'q','R', 'r','S', 's','T', 't','U', 'u','V', 'v','W', 'w','X', 'x','Y', 'y','Z' 'z']
def cript(s):
for i in range(0,len(s)):
a=dict.index(s[i])
if a<26:
s[i]=dict[a+26]
else:
s[i]=dict[a-26]
return s
print cript('Hello')
Error line 6
s[i]= dict[a+26]
TypeError: 'str' object does not support item assignment python
Python does not allow you to swap out characters in a string for another one; strings are immutable. What you'll need to do is create a totally different string and return that instead.
dict=['A', 'a','B', 'b','C', 'c','D', 'd','E', 'e','F', 'f','G', 'g','H', 'h','I', 'i','J', 'j','K', 'k','L', 'l','M', 'm','N', 'n','P', 'o','P', 'p','Q', 'q','R', 'r','S', 's','T', 't','U', 'u','V', 'v','W', 'w','X', 'x','Y', 'y','Z' 'z']
def cript(s):
crypt_s = ""
for i in range(0,len(s)):
a=dict.index(s[i])
if a<26:
crypt_s += dict[a+26]
else:
crypt_s += dict[a-26]
return crypt_s
print cript('Hello')
Of course, there may be other issues with the code, but that will solve that specific error message.
Strings are immutable objects, meaning they can't be modified in place (you'd have to return a new string and reassign it).
s[i] = dict[a + 26]
is trying to reassign a value in the string
Here is an easier to see example
>>> astring = "Hello"
>>> astring[0] = "a"
Traceback (most recent call last):
File "<pyshell#1>", line 1, in <module>
astring[0] = "a"
TypeError: 'str' object does not support item assignment
def game():
list = ["pomme", "anniversaire", "table", "travail", "amies", "enfants"]
ran = random.randint(0, (len(list)-1)/2)
mot = list[ran]
length = len(mot)
for x in range(0, length-1):
if length % 2 ==0:
a = int(random.randint(-length/2, length/2))
else:
a = int(random.randint(-(length-1)/2, (length-1)/2))
mot[x] = mot[x+a]

Weird bug in python project

I'm working on a big project in Python, and I've run into a bizarre error I can't explain. In one of my classes, I have a private method being called during instantiation:
def _convertIndex(self, dimInd, dimName):
'''Private function that converts numbers to numbers and non-integers using
the subclass\'s convertIndex.'''
print dimInd, ' is dimInd'
try:
return int(dimName)
except:
if dimName == '*':
return 0
else:
print self.param.sets, ' is self.param.sets'
print type(self.param.sets), ' is the type of self.param.sets'
print self.param.sets[dimInd], ' is the param at dimind'
return self.param.sets[dimInd].value(dimName)
What it's printing out:
0 is dimInd
[<coremcs.SymbolicSet.SymbolicSet object at 0x10618ad90>] is self.param.sets
<type 'list'> is the type of self.param.sets
<SymbolicSet BAZ=['baz1', 'baz2', 'baz3', 'baz4']> is the param at dimind
======================================================================
ERROR: testParameterSet (GtapTest.TestGtapParameter)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/Users/myuser/Documents/workspace/ilucmc/gtapmcs/test/GtapTest.py", line 116, in testParameterSet
pset = ParameterSet(prmFile, dstFile, GtapParameter)
File "/Users/myuser/Documents/workspace/ilucmc/coremcs/ParameterSet.py", line 103, in __init__
self.distroDict, corrDefs = AbsBaseDistro.readFile(distFile, self.paramDict)
File "/Users/myuser/Documents/workspace/ilucmc/coremcs/Distro.py", line 359, in readFile
distro = cls.parseDistro(param, target, distroType, args)
File "/Users/myuser/Documents/workspace/ilucmc/coremcs/Distro.py", line 301, in parseDistro
return cls(param, target, distro, dim_names, argDict)
File "/Users/myuser/Documents/workspace/ilucmc/coremcs/Distro.py", line 150, in __init__
self.dim_indxs = list(starmap(self._convertIndex, enumerate(dim_names))) # convert to numeric values and save in dim_indxs
File "/Users/myuser/Documents/workspace/ilucmc/coremcs/Distro.py", line 194, in _convertIndex
print self.param.sets[dimInd], ' is the param at dimind'
IndexError: list index out of range
Obviously this isn't the code for the whole class, but it represents something that I don't understand. The error is coming when I index into self.param.sets. Apparently, dimInd is out of range. the problem is, dimInd is 0, and self.param.sets is a list of length 1 (as shown from the print statements), so why can't I index into it?
EDIT: For what it's worth, the __init__ method looks like this:
'''
Stores a definitions of a distribution to be applied to a header variable.
See the file setup/gtap/DistroDoc.txt for the details.
'''
def __init__(self, param, target, distType, dim_names, argDict):
self.name = param.name
self.dim_names = dim_names
self.dim_indxs = []
self.target = target.lower() if target else None
self.distType = distType.lower() if distType else None
self.rv = None
self.argDict = {}
self.modifier = defaultdict(lambda: None)
self.param = param
# Separate args into modifiers and distribution arguments
for k, v in argDict.iteritems():
if k[0] == '_': # modifiers start with underscore
self.modifier[k] = v
else:
self.argDict[k] = v # distribution arguments do not have underscore
if self.target == 'index':
print dim_names
self.dim_indxs = list(starmap(self._convertIndex, enumerate(dim_names))) # convert to numeric values and save in dim_indxs
if distType == 'discrete':
entries = self.modifier['_entries']
if not entries:
raise DistributionSpecError("Not enough arguments given to discrete distribution.")
modDict = {k[1:]: float(v) for k, v in self.modifier.iteritems() if k[1:] in getOptionalArgs(DiscreteDist.__init__)}
self.rv = DiscreteDist(entries, **modDict)
return
sig = DistroGen.signature(distType, self.argDict.keys())
gen = DistroGen.generator(sig)
if gen is None:
raise DistributionSpecError("Unknown distribution signature %s" % str(sig))
self.rv = gen.makeRV(self.argDict) # generate a frozen RV with the specified arguments
self.isFactor = gen.isFactor

load parameters from a file in Python

I am writing a Python class to model a process and I want to initialized the parameters from a file, say 'input.dat'. The format of the input file looks like this.
'input.dat' file:
Z0: 0 0
k: 0.1
g: 1
Delta: 20
t_end: 300
The code I wrote is the following. It works but appears redundant and inflexible. Is there a better way to do the job? Such as a loop to do readline() and then match the keyword?
def load(self,filename="input.dat"):
FILE = open(filename)
s = FILE.readline().split()
if len(s) is 3:
self.z0 = [float(s[1]),float(s[2])] # initial state
s = FILE.readline().split()
if len(s) is 2:
self.k = float(s[1]) # kappa
s = FILE.readline().split()
if len(s) is 2:
self.g = float(s[1])
s = FILE.readline().split()
if len(s) is 2:
self.D = float(s[1]) # Delta
s = FILE.readline().split()
if len(s) is 2:
self.T = float(s[1]) # end time
Assuming the params are coming from a safe place (made by you or users, not the internet), just make the parameters file a Python file, params.py:
Z0 = (0, 0)
k = 0.1
g = 1
Delta = 20
t_end = 300
Then in your code all you need is:
import params
fancy_calculation(10, k=params.k, delta=params.Delta)
The beauty of this is two-fold: 1) simplicity, and 2) you can use the power of Python in your parameter descriptions -- particularly useful here, for example:
k = 0.1
Delta = 20
g = 3 * k + Delta
Alternatively, you could use Python's built-in JSON or ConfigParser .INI parser modules.
If you are open to some other kind of file where you can keep your parameters, I would suggest you to use a YAML file.
The Python library is PyYAML. This is how you can easily use it with Python.
For a better introduction, look at this Wikipedia article: http://en.wikipedia.org/wiki/YAML.
The benefit is you can read the parameter values as lists or maps.
You would love it!
Try the following:
def load(self, filename="input.dat"):
d = {"Z0": "z0", "k": "k", "g": "g", "Delta": "D", "t_end": "T"}
FILE = open(filename)
for line in FILE:
name, value = line.split(":")
value = value.strip()
if " " in value:
value = map(float, value.split())
else:
value = float(value)
setattr(self, d[name], value)
Proof that it works:
>>> class A(object): pass
...
>>> a = A()
>>> load(a)
>>> a.__dict__
{'k': 0.10000000000000001, 'z0': [0.0, 0.0], 'D': 20.0, 'g': 1.0, 'T': 300.0}
As others have mentioned, in Python you can create object attributes dynamically "on the fly". That means you could do something like the following to create Params objects as they're read-in. I've tried to make the code as data-driven as possible, so relatively flexible.
# maps label to attribute name and types
label_attr_map = {
"Z0:": ["z0", float, float],
"k:": [ "k", float],
"g:": [ "g", float],
"Delta:": [ "D", float],
"t_end:": [ "T", float]
}
class Params(object):
def __init__(self, input_file_name):
with open(input_file_name, 'r') as input_file:
for line in input_file:
row = line.split()
label = row[0]
data = row[1:] # rest of row is data list
attr = label_attr_map[label][0]
datatypes = label_attr_map[label][1:]
values = [(datatypes[i](data[i])) for i in range(len(data))]
self.__dict__[attr] = values if len(values) > 1 else values[0]
params = Params('input.dat')
print 'params.z0:', params.z0
print 'params.k:', params.k
print 'params.g:', params.g
print 'params.D:', params.D
print 'params.T:', params.T
Output:
params.z0: [0.0, 0.0]
params.k: 0.1
params.g: 1.0
params.D: 20.0
params.T: 300.0
Perhaps this might give you what you need:
def load(self,filename='input.dat'):
with open(filename) as fh:
for line in fh:
s = line.split()
if len(s) == 2:
setattr(self,s[1],s[2])
elif len(s) == 3:
setattr(self,s[1],s[2:])
I also didn't include any error checking, but setattr is very handy.
Something like this:
def load(self,filename="input.dat"):
# maps names to number of fields they need
# only necessary for variables with more than 1 field
argmap = dict(Z0=2)
# maps config file names to their attribute names on the object
# if name is the same both places, no need
namemap = dict(Z0="z0", Delta="D", t_end="T")
with open(filename) as FILE:
for line in FILE:
s = line.split()
var = s[0].rstrip(":")
try:
val = [float(x) for x in s[1:]]
except ValueError:
continue
if len(val) == varmap.get(var, 1):
if len(val) == 1:
val = val[0]
setattr(self, namemap.get(var, var), val)
Python objects have a built-in __dict__ member. You can modify it, and then refer to properties as obj.key.
class Data(object):
def __init__(self, path='infile.dat'):
with open(path, 'r') as fo:
for line in fo.readlines():
if len(line) < 2: continue
parts = [s.strip(' :\n') for s in line.split(' ', 1)]
numbers = [float(s) for s in parts[1].split()]
# This is optional... do you want single values to be stored in lists?
if len(numbers) == 1: numbers = numbers[0]
self.__dict__[parts[0]] = numbers
# print parts -- debug
obj = Data('infile.dat')
print obj.g
print obj.Delta
print obj.Z0
At the end of this, we print out a few of the keys. Here's the output of those.
1.0
20.0
[0.0, 0.0]
For consistency, you can remove the line marked "optional" in my code, and have all objects in lists -- regardless of how many elements they have. That will make using them quite a bit easier, because you never have to worry about obj.g[0] returning an error.
Here's another one
def splitstrip(s):
return s.split(':')[1].strip()
with open('input.dat','r') as f:
a.z0 = [float(x) for x in splitstrip(f.readline()).split(' ')]
a.k, a.g, a.D, a.T = tuple([float(splitstrip(x)) for x in f.read().rstrip().split('\n')])
;)

Categories

Resources