# Sage cannot find user defined contains method

What am I doing wrong?

class Circle:
def __init__(self, m, r):
self.xm = m
self.ym = m
self.r = r

def __str__(self):
return "Circle"

def __contains__(self, point):
return True


In Sage:

sage: c = Circle((0,0),1)
sage: print (0,0) in c
TypeError: argument of type 'instance' is not iterable

edit retag close merge delete

Sort by » oldest newest most voted

(Just to be clear, this is just a basic Python programming question and is not in any way specific to Sage. I point this out just because it's good for beginners with Sage to remember that when they're using Sage they're programming in Python, so most problems with code that does even use classes/functions from Sage can also be answered through general Python programming resources).

In any case, it works for me:

sage: class Circle:
....:     def __init__(self, m, r):
....:         self.xm = m
....:         self.ym = m
....:         self.r = r
....:
....:     def __str__(self):
....:         return "Circle"
....:
....:     def __contains__(self, point):
....:         return True
....:
sage: c = Circle((0,0),1)
True


It's possible that, however you input your Circle class, you didn't properly indent the __contains__ method. For example:_print (0, 0) in c

sage: class Circle:
....:     def __init__(self, m, r):
....:         self.xm = m
....:         self.ym = m
....:         self.r = r
....:
....:     def __str__(self):
....:         return "Circle"
....:
....: def __contains__(self, point):
....:     return True
....:
sage: c = Circle((0,0),1)
sage: print (0, 0) in c
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-6-ed9abe6f29a8> in <module>()
----> 1 print (Integer(0), Integer(0)) in c

TypeError: argument of type 'instance' is not iterable


With Python you always need to be careful about indentation, especially when defining classes and functions, and other "block level" statements, like if, while, for, etc. In the above example def __contains__(self, point) is not indented like the other methods, so the Python interpreter has no way to distinguish that it's a method of class Circle and not just some function that happens to be called __contains__.

You can understand the error message:

TypeError: argument of type 'instance' is not iterable


as follows: Here the in keyword is actually an operator, where the tuple (0, 0) and your instance of the Circle class c are the operands. When you do A in B in Python one of a couple different things can happen. To simplify a bit, the Python interpreter first checks if B has a B.__contains__ method, and if so it calls it, passing A as the argument. However, if there is not a __contains__ method, the fallback is to try B.__iter__ to see if B can be iterated over using Python's iterator protocol. If so, it will iterate over B until B's iterator returns A, in which case the result of the in operator will be True. Otherwise, if it exhausts every element of B (which, dangerously, may be infinite) it will return False. Since the fallback for in is to try to treat B as iterable, you get the above error message. There is no fallback beyond that, though maybe a better error message would be something like "A does not support the 'in' operator; it does not implement __contains__ or __iter__".

As a nitpick, by convention it's still better, when defining a class with no base class, to define it like class Circle(object), to ensure that the class has the object base class. Without it, on Python 2, you create what's referred to as an "old-style class" which works subtly different. This is only needed on Python 2 (which Sage still uses by default). On Python 3 (which Sage will be moving to soon hopefully), object is already the base class of all classes, and so the syntax you used is fine. Regardless, this was not the cause of your problem in this case.

One final point, when you wrote:

sage: print (0, 0) in c


the print statement here is generally not necessary when working at the interactive prompt. In the interactive prompt you can just directly enter any expression and its result will be output, like:

sage: (0, 0) in c
True


You only need print if you're writing a script that needs to print specific results to the screen. Also, get used to using print() with parentheses as it will become the default in Python 3. print (0, 0) in c actually has a very different result in Python 3, and is explicitly not what you want.

more