infinite recursion when printing instance in python - python

I get a Runtime error (maximum recursion) when I execute the following code.
I'm trying to generate a list of instances; then I would like to print each one. I'm not sure what's going on here.
Anyway, what is the correct way to access each instance from the instance list?
I do realize I'm using a string of digits to name the instances and this is not cool. But say each number on the list is associated with a bunch of information. Then having attributes for each could make things accessible. I tried using a dict but I end up with nested dicts and I just didn't like it.
Thanks in advance.
class MyClass(object):
def __str__(self):
stuff= str(self)
return stuff
mylist = ['1234567','8910111','1213144','7654321']
inslist = [MyClass() for i in mylist]
print inslist[0]

The problem is in your __str__. If you call str(self), it will call itself. I think you meant was this:
class MyClass(object):
def __init__(self, i):
self.i = i
def __str__(self):
return str(self.i)
mylist = ['1234567','8910111','1213144','7654321']
inslist = [MyClass(i) for i in mylist]
print inslist[0]

Related

Python: how to recursively apply __str__ in print

I had some problem printing user-defined class instance in containers. In short, if my code is:
class A():
def __str__(self):
return 'abc'
class B():
def __str__(self):
return str(A())
a,b=A(),B()
C=[[a],b]
print(C)
Then the output should be like:[[<__main__.A object at 0x02D99910>], <__main__.B object at 0x02DD5030>], but I want it to recursively apply customized __str__ and works even in nested lists or classes, i.e. I want the output to be [['abc'],'abc']. Any pythonic way to do?
#Blckknight should have submitted as an answer, because that seems to be the correct answer (it worked for my very similar question):
**override repr() instead of str()
def __repr__(self):
return "abc"
you can also add this for completeness
def str(self):
return self.repr()
#Blckknight if you want to resubmit as an answer for the points, you should. I wish I could just add a comment but i don't have the reputation to do that.
You would need to override the __str__ method of list, not A or B, since that's where the recursion needs to begin. That is, to turn a list into a string, you need to recursively turn each object in the list into a string. Unfortunately, you cannot do that. The best you can do is write a separate function, something like
def list_to_str(l):
if isinstance(l, list):
return "[%s]" % (", ".join(map(list_to_str, l)),)
else:
return "'%s'" % (str(l),)
print(list_to_str(C))

What is the proper way to share a list between class instances?

I have this example code
my_list = ["a","b","c","d"]
class A:
def __repr__(self):
return ', '.join(my_list)
def add(self, num):
my_list.append(num)
class_list = []
for x in range(5):
class_list.append(A())
class_list[x].add("class_%s" % (x))
print class_list[x]
The non-example code of mine is more complicated, but the idea is that I have multiple instances of the classes off doing a "thing". The global my_list is utilized across all instances. When certain logic is met within a class, that instance will modify the list. The rest of the classes will utilize that list to perform their logic as well. Any instance can add to the list, and all instances should be able to utilize the updated value.
Now in this example, the my_list is shared, but is this the correct way to do it?
A class attribute is usually better than a global, because then they're just sharing it with each other, rather than with everyone in the world.
To do that, move the my_list = ["a","b","c","d"] line under the class A:, and change every reference to my_list to self.my_list or A.my_list:
class A(object):
shared_list = []
def add(self, num):
self.my_list.append(num)
However, an instance attribute is often even better. If you assign the same list to a bunch of different variables, it's still just one list; changing it affects all those variables. So, you can do something like this:
class A(object):
def __init__(self, shared_list):
self.shared_list = shared_list
def add(self, num):
self.shared_list.append(num)
Now it's up to the code that uses the A objects to decide whether to give them all the same list. You can even create 20 instances that share one list, and 10 that share a different one:
list1 = []
group1 = [A(list1) for _ in range(20)
list2 = []
group2 = [A(list2) for _ in range(10)
The question is whether the caller, or the A class, or nobody at all is the one who should be making the decision of how "shared" the list is. The answer is different for different applications, so it's hard to give an answer for an abstract example with names like A and my_list.

What does "bound method" error mean when I call a function?

I am creating a word parsing class and I keep getting a
bound method Word_Parser.sort_word_list of <__main__.Word_Parser instance at 0x1037dd3b0>
error when I run this:
class Word_Parser:
"""docstring for Word_Parser"""
def __init__(self, sentences):
self.sentences = sentences
def parser(self):
self.word_list = self.sentences.split()
def sort_word_list(self):
self.sorted_word_list = self.word_list.sort()
def num_words(self):
self.num_words = len(self.word_list)
test = Word_Parser("mary had a little lamb")
test.parser()
test.sort_word_list()
test.num_words()
print test.word_list
print test.sort_word_list
print test.num_words
There's no error here. You're printing a function, and that's what functions look like.
To actually call the function, you have to put parens after that. You're already doing that above. If you want to print the result of calling the function, just have the function return the value, and put the print there. For example:
print test.sort_word_list()
On the other hand, if you want the function to mutate the object's state, and then print the state some other way, that's fine too.
Now, your code seems to work in some places, but not others; let's look at why:
parser sets a variable called word_list, and you later print test.word_list, so that works.
sort_word_list sets a variable called sorted_word_list, and you later print test.sort_word_list—that is, the function, not the variable. So, you see the bound method. (Also, as Jon Clements points out, even if you fix this, you're going to print None, because that's what sort returns.)
num_words sets a variable called num_words, and you again print the function—but in this case, the variable has the same name as the function, meaning that you're actually replacing the function with its output, so it works. This is probably not what you want to do, however.
(There are cases where, at first glance, that seems like it might be a good idea—you only want to compute something once, and then access it over and over again without constantly recomputing that. But this isn't the way to do it. Either use a #property, or use a memoization decorator.)
This problem happens as a result of calling a method without brackets. Take a look at the example below:
class SomeClass(object):
def __init__(self):
print 'I am starting'
def some_meth(self):
print 'I am a method()'
x = SomeClass()
''' Not adding the bracket after the method call would result in method bound error '''
print x.some_meth
''' However this is how it should be called and it does solve it '''
x.some_meth()
You have an instance method called num_words, but you also have a variable called num_words. They have the same name. When you run num_words(), the function replaces itself with its own output, which probably isn't what you want to do. Consider returning your values.
To fix your problem, change def num_words to something like def get_num_words and your code should work fine. Also, change print test.sort_word_list to print test.sorted_word_list.
For this thing you can use #property as an decorator, so you could use instance methods as attributes. For example:
class Word_Parser:
def __init__(self, sentences):
self.sentences = sentences
#property
def parser(self):
self.word_list = self.sentences.split()
#property
def sort_word_list(self):
self.sorted_word_list = self.word_list.sort()
#property
def num_words(self):
self.num_words = len(self.word_list)
test = Word_Parser("mary had a little lamb")
test.parser()
test.sort_word_list()
test.num_words()
print test.word_list
print test.sort_word_list
print test.num_words
so you can use access the attributes without calling (i.e., without the ()).
I think you meant print test.sorted_word_list instead of print test.sort_word_list.
In addition list.sort() sorts a list in place and returns None, so you probably want to change sort_word_list() to do the following:
self.sorted_word_list = sorted(self.word_list)
You should also consider either renaming your num_words() function, or changing the attribute that the function assigns to, because currently you overwrite the function with an integer on the first call.
The syntax problem is shadowing method and variable names. In the current version sort_word_list() is a method, and sorted_word_list is a variable, whereas num_words is both. Also, list.sort() modifies the list and replaces it with a sorted version; the sorted(list) function actually returns a new list.
But I suspect this indicates a design problem. What's the point of calls like
test.parser()
test.sort_word_list()
test.num_words()
which don't do anything? You should probably just have the methods figure out whether the appropriate counting and/or sorting has been done, and, if appropriate, do the count or sort and otherwise just return something.
E.G.,
def sort_word_list(self):
if self.sorted_word_list is not None:
self.sorted_word_list = sorted(self.word_list)
return self.sorted_word_list
(Alternately, you could use properties.)
Your helpful comments led me to the following solution:
class Word_Parser:
"""docstring for Word_Parser"""
def __init__(self, sentences):
self.sentences = sentences
def parser(self):
self.word_list = self.sentences.split()
word_list = []
word_list = self.word_list
return word_list
def sort_word_list(self):
self.sorted_word_list = sorted(self.sentences.split())
sorted_word_list = self.sorted_word_list
return sorted_word_list
def get_num_words(self):
self.num_words = len(self.word_list)
num_words = self.num_words
return num_words
test = Word_Parser("mary had a little lamb")
test.parser()
test.sort_word_list()
test.get_num_words()
print test.word_list
print test.sorted_word_list
print test.num_words
and returns:
['mary', 'had', 'a', 'little', 'lamb']
['a', 'had', 'lamb', 'little', 'mary']
5
Thank you all.
Bound method error also occurs (in a Django app for instnce) , if you do a thing as below:
class Products(models.Model):
product_category = models.ForeignKey(ProductCategory, on_delete=models.Protect)
def product_category(self)
return self.product_category
If you name a method, same way you named a field.

Python - Call an object from a list of objects

I have a class, and I would like to be able to create multiple objects of that class and place them in an array. I did it like so:
rooms = []
rooms.append(Object1())
...
rooms.append(Object4())
I then have a dict of functions, and I would like to pass the object to the function. However, I'm encountering some problems..For example, I have a dict:
dict = {'look': CallLook(rooms[i])}
I'm able to pass it into the function, however; in the function if I try to call an objects method it gives me problems
def CallLook(current_room)
current_room.examine()
I'm sure that there has to be a better way to do what I'm trying to do, but I'm new to Python and I haven't seen a clean example on how to do this. Anyone have a good way to implement a list of objects to be passed into functions? All of the objects contain the examine method, but they are objects of different classes. (I'm sorry I didn't say so earlier)
The specific error states: TypeError: 'NoneType' object is not callable
Anyone have a good way to implement a list of objects to be passed into functions? All of the objects contain the examine method, but they are objects of different classes. (I'm sorry I didn't say so earlier)
This is Python's plain duck-typing.
class Room:
def __init__(self, name):
self.name = name
def examine(self):
return "This %s looks clean!" % self.name
class Furniture:
def __init__(self, name):
self.name = name
def examine(self):
return "This %s looks comfortable..." % self.name
def examination(l):
for item in l:
print item.examine()
list_of_objects = [ Room("Living Room"), Furniture("Couch"),
Room("Restrooms"), Furniture("Bed") ]
examination(list_of_objects)
Prints:
This Living Room looks clean!
This Couch looks comfortable...
This Restrooms looks clean!
This Bed looks comfortable...
As for your specific problem: probably you have forgotten to return a value from examine()? (Please post the full error message (including full backtrace).)
I then have a dict of functions, and I would like to pass the object to the function. However, I'm encountering some problems..For example, I have a dict:
my_dict = {'look': CallLook(rooms[i])} # this is no dict of functions
The dict you have created may evaluate to {'look': None} (assuming your examine() doesn't return a value.) Which could explain the error you've observed.
If you wanted a dict of functions you needed to put in a callable, not an actual function call, e.g. like this:
my_dict = {'look': CallLook} # this is a dict of functions
if you want to bind the 'look' to a specific room you could redefine CallLook:
def CallLook(current_room)
return current_room.examine # return the bound examine
my_dict = {'look': CallLook(room[i])} # this is also a dict of functions
Another issue with your code is that you are shadowing the built-in dict() method by naming your local dictionary dict. You shouldn't do this. This yields nasty errors.
Assuming you don't have basic problems (like syntax errors because the code you have pasted is not valid Python), this example shows you how to do what you want:
>>> class Foo():
... def hello(self):
... return 'hello'
...
>>> r = [Foo(),Foo(),Foo()]
>>> def call_method(obj):
... return obj.hello()
...
>>> call_method(r[1])
'hello'
Assuming you have a class Room the usual way to create a list of instances would be using a list comprehension like this
rooms = [Room() for i in range(num_rooms)]
I think there are some things you may not be getting about this:
dict = {'look': CallLook(rooms[i])}
This creates a dict with just one entry: a key 'look', and a value which is the result of evaluating CallLook(rooms[i]) right at the point of that statement. It also then uses the name dict to store this object, so you can no longer use dict as a constructor in that context.
Now, the error you are getting tells us that rooms[i] is None at that point in the programme.
You don't need CallLook (which is also named non-standardly) - you can just use the expression rooms[i].examine(), or if you want to evaluate the call later rooms[i].examine.
You probably don't need the dict at all.
That is not a must, but in some cases, using hasattr() is good... getattr() is another way to get an attribute off an object...
So:
rooms = [Obj1(),Obj2(),Obj3()]
if hasattr(rooms[i], 'examine'):#First check if our object has selected function or attribute...
getattr(rooms[i], 'examine') #that will just evaluate the function do not call it, and equals to Obj1().examine
getattr(rooms[i], 'examine')() # By adding () to the end of getattr function, we evalute and then call the function...
You may also pass parameters to examine function like:
getattr(rooms[i], 'examine')(param1, param2)
I'm not sure of your requirement, but you can use dict to store multiple object of a class.
May be this will help,
>>> class c1():
... print "hi"
...
hi
>>> c = c1()
>>> c
<__main__.c1 instance at 0x032165F8>
>>> d ={}
>>> for i in range (10):
... d[i] = c1()
...
>>> d[0]
<__main__.c1 instance at 0x032166E8>
>>> d[1]
<__main__.c1 instance at 0x032164B8>
>>>
It will create a object of c1 class and store it in dict. Obviously, in this case you can use list instead of dict.

Why is this simple python class not working?

I'm trying to make a class that will get a list of numbers then print them out when I need. I need to be able to make 2 objects from the class to get two different lists. Here's what I have so far
class getlist:
def newlist(self,*number):
lst=[]
self.number=number
lst.append(number)
def printlist(self):
return lst
Sorry I'm not very clear, I'm a bit new to oop, can you please help me cos I don't know what I'm doing wrong. Thanks.
In Python, when you are writing methods inside an object, you need to prefix all references to variables belonging to that object with self. - like so:
class getlist:
def newlist(self,*number):
self.lst=[]
self.lst += number #I changed this to add all args to the list
def printlist(self):
return self.lst
The code you had before was creating and modifying a local variable called lst, so it would appear to "disappear" between calls.
Also, it is usual to make a constructor, which has the special name __init__ :
class getlist:
#Init constructor
def __init__(self,*number):
self.lst=[]
self.lst += number #I changed this to add all args to the list
def printlist(self):
return self.lst
Finally, use like so
>>> newlist=getlist(1,2,3, [4,5])
>>> newlist.printlist()
[1, 2, 3, [4,5]]
You should use "self.lst" instead of "lst". Without the "self", it's just internal variable to current method.

Categories