0

So I have the following directory setup:

  • Project
    • Misc Packages and Modules
    • resources
      • shapes
        • Rectangle.py
        • Rectangle2D.py

Rectangle is just a conceptual rectangle that isn't ever drawn (using Tkinter) but is there for collision and bounds. Rectangle2D is used as a subclass to draw or fill a Rectangle. The classes before I got the error were as followed:

Rectangle.py

class Rectangle(object):
    _x = -1
    _y = -1
    _w = -1
    _h = -1

    def __init__(self, x, y, w, h):
        self._x = x
        self._y= y
        self._w = w
        self._h = h

    def intersects(self, other):
        if isinstance(other, Rectangle):
            if (self._x + self._w) > other._x > self._x and (self._y + self._h) > other._y > self._y:
                return True
            elif (self._x + self._w) > (other._x + other._w) > self._x and (self._y + self._h) > other._y > self._y:
                return True
            elif (self._x + self._w) > other._x > self._x and (other._y + other._h) > self._y > other._y:
                return True
            elif (self._x + self._w) > (other._x + other._w) > self._x and (other._y + other._h) > self._y > other._y:
                return True
            else:
                return False
        else:
            return False

    def __eq__(self, other):
        return self._x == other._x and self._y == other._y and self._w == other._w and self._h == other._h

Rectangle2D.py

from tkinter import Canvas
from .Rectangle import Rectangle

class Rectangle2D(Rectangle):
    def __init__(self, x, y, w, h):
        super(Rectangle2D, self).__init__(x, y, w, h)
        self.color = 'black'
        self.id = None

    def draw_rect(self, canvas):
        if isinstance(canvas, Canvas):
            self.id = canvas.create_rectangle(self._x, self._y, self._x + self._w, self._y + self._h, outline=self.color)
            return True
        else:
            print("Improper Parameter Type")
            return False

    def fill_rect(self, canvas):
        if isinstance(canvas, Canvas):
            self.id = canvas.create_rectangle(self._x, self._y, self._x + self._w, self._y + self._h, fill=self.color)
            return True
        else:
            print("Improper Parameter Type")
            return False

Everything was working fine until I wanted to add a method to Rectangle.py that would return a Rectangle2D.py. This record the following line to be added to Rectangle.py in addition to the method:

from .Rectangle2D import Rectangle2D

This resulted in the following error:

from .Rectangle import Rectangle
ImportError: cannot import name 'Rectangle'

What is causing this error and how do I fix it? Also note I am running Python 3.6

Ryan
  • 727
  • 2
  • 14
  • 31
  • circular import issue, most likely. Why not just put `Rectangle` and `Rectangle2D` in the same module? – juanpa.arrivillaga Apr 24 '17 at 23:03
  • @wim AFAIK, OP claims they added `from .Rectangle2D import Rectangle2D` to `Rectangle.py` and `Rectangle2D.py` also has `from .Rectangle import Rectangle` isn't that a classic circular import? Now I'm just confused... – juanpa.arrivillaga Apr 24 '17 at 23:06
  • @wim No, `Rectangle` does import from `Rectangle2D`, I just said it at the end and showed the classes before I got the error. I added the import statement at the end of the post. – Ryan Apr 24 '17 at 23:07
  • You are correct. I was looking at the code in the state as written, and glossed over the part where they said they added some lines. – wim Apr 24 '17 at 23:07
  • 1
    Just put `Rectangle` and `Rectangle2D` in the same file, and to follow conventions, that can just be called `rectangle.py` (lower-cased!) – juanpa.arrivillaga Apr 24 '17 at 23:07
  • 1
    Unlike Java, you don't need to organize your code into files each containing a single class with the same name as the file, and instance variables are not declared at class scope (that creates static variables). – user2357112 Apr 24 '17 at 23:09
  • @juanpa.arrivillaga That's not always the best way to resolve these. It is actually possible to do it while keeping the circular import and existing structure. Although in this case it does seem sensible! – wim Apr 24 '17 at 23:10
  • I think the real question is why `Rectangle` would need to return one of its sublcasses. Whatever reason you have for that can likely be moved elsewhere. You don't instantiate `Rectangle` anywhere do you? Its just implementing abstract functionality for children. – tdelaney Apr 24 '17 at 23:11
  • For a discussion of circular imports see http://stackoverflow.com/questions/22187279/python-circular-importing – Paul Cornelius Apr 24 '17 at 23:14
  • Try some of the things mentioned in the second answer on this article (the one under the accepted one) http://stackoverflow.com/questions/7336802/how-to-avoid-circular-imports-in-python Specifically using a package or explicit syntax for importing the module. – NotARobot Apr 24 '17 at 23:16
  • Isn't it bad OOD to have Rectangle need to know about Rectangle2D and Rectangle2D know about Rectangle? Seems like you are creating unnecessary interdependence. Depending on what you are trying to do, it seems it would end up being a code smell like "Inappropriate Intimacy". What exactly were you trying to do that Rectangle should return a Rectangle2D and maybe I could recommend a different abstraction for you. – RobertB Apr 24 '17 at 23:49
  • Two other observations: 1) instead of `isinstance` just `try:` to do the intersection and catch the `AttributeError`. This enables "duck typing". 2) The _ before _x means that it is private (by convention). But why make the attributes private anyway? It seems unnecessary. Plus you are accessing other object's privates (e.g. `other._x`) a no-no. Plus should Rectangle2D know about Rectangle's privates? I would also say no. This infers to me that they should not be private. – RobertB Apr 25 '17 at 00:13
  • @RobertB The `isinstance` bit makes sense, and I agree with what you are saying. I'm kind of combining Java ideas (which is what I'm coming from) and Python ideas. In Java I would make those variables private and use accessor methods to get the values but in Python you have access to all the variables so it is a little weird for me lol. And what I was doing was attempting to perform a shortcut for something unnecessary so I just scrapped it – Ryan Apr 25 '17 at 01:58
  • Yeah it isn't very pythonic to check all your parameters' classes. I can't remember the last time I used isinstance. – RobertB Apr 25 '17 at 07:24

0 Answers0