ASKSAGE: Sage Q&A Forum - RSS feedhttps://ask.sagemath.org/questions/Q&A Forum for SageenCopyright Sage, 2010. Some rights reserved under creative commons license.Fri, 05 Jul 2019 17:56:36 +0200Sage cannot find user defined contains methodhttps://ask.sagemath.org/question/47073/sage-cannot-find-user-defined-contains-method/ What am I doing wrong?
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
In Sage:
sage: c = Circle((0,0),1)
sage: print (0,0) in c
TypeError: argument of type 'instance' is not iterable
Fri, 05 Jul 2019 11:18:22 +0200https://ask.sagemath.org/question/47073/sage-cannot-find-user-defined-contains-method/Answer by Iguananaut for <p>What am I doing wrong?</p>
<pre><code>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
</code></pre>
<p>In Sage:</p>
<pre><code>sage: c = Circle((0,0),1)
sage: print (0,0) in c
TypeError: argument of type 'instance' is not iterable
</code></pre>
https://ask.sagemath.org/question/47073/sage-cannot-find-user-defined-contains-method/?answer=47079#post-id-47079(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](https://docs.python.org/3/tutorial/classes.html#iterators). 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.Fri, 05 Jul 2019 17:56:36 +0200https://ask.sagemath.org/question/47073/sage-cannot-find-user-defined-contains-method/?answer=47079#post-id-47079