(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[0]
....: self.ym = m[1]
....: 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[0]
....: self.ym = m[1]
....: 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.