0

So for a line class I'm doing, I keep getting an error that says

AttributeError: Line instance has no attribute 'point0'

I'm declaring the line like this:

def __init__(self, point0, point1):
    self.x = point0
    self.y = point1

def __str__(self):
    return '%d %d' % (int(round(self.point0)), int(round(self.point1)))

And I get the x and y from my point class which should already be float values so I don't need to check for an error in my init method however I do check to see if point0 and point1 are floats in my rotate method:

def rotate(self, a):
        if not isinstance(a, float) or not isinstance(self.point0, float) or not isinstance(self.point1, float):
            raise Error("Parameter \"a\" illegal.")
        self.point0 = math.cos(a) * self.point0 - math.sin(a) * self.point1
        self.point1 = math.sin(a) * self.point0 + math.cos(a) * self.point1

So why does python keep saying that it has no attribute point0? I also tried changing my init method to look like this:

def __init__(self, point0, point1):
    self.point0 = point0
    self.point1 = point1

But when I do that the error says point0 has no attribute float. So why do I keep getting this error? Here's the code I'm using to test:

p0 = Point(0.0, 1.0)
p1 = Point(2.0, 3.0)
line = Line(p0,p1)
print line
John Kyle
  • 117
  • 2
  • 8
  • It would be esier if you can provide the whole code you have already written. Thus its easier for debugging. – Sens4 Mar 08 '16 at 20:56
  • Alright I updated it to show my entire code. – John Kyle Mar 08 '16 at 20:59
  • Don't bother checking the types of all your arguments. Python's philosophy is that we're all responsible enough to read the documentation and pass arguments of the correct type. `isinstance` is used primarily to modify behavior depending on whether an argument is, say, a string or a tuple (cf `str.endswith`), not to raise errors if a value isn't the right type. – chepner Mar 08 '16 at 22:05

4 Answers4

1

I'm curious... how much do you know about scope in Python?

In your class, you have a member variable named x and another named y. Your init function accepts an argument called point0 and another called point1. It saves point0 in the x member variable, and point1 in y. Then, in your rotate function, you attempt to access a variable called point0. Do you see the problem?

An important thing to understand when programming (and this is true in most programming languages, if not all of them) is that the name of an argument doesn't affect the name of that data elsewhere. I can pass a variable called foo into a function that takes an argument called bar. In that function, I have to refer to the data as bar because that's the name of the variable. Later, after I've called that function, the name of the variable is still foo, because only the variable inside the function is called bar. Does that make sense?

Blarghedy
  • 47
  • 8
  • I just don't know what self does in python because I never saw it in any other programming languages. – John Kyle Mar 08 '16 at 21:02
  • Its basically a handle to your instance of the class you are using it in. Like in c# `this`. – Sens4 Mar 08 '16 at 21:05
  • I still have no idea what to do. If I change my point0 back to x I get another error that says "AttributeError: Point instance has no attribute '__float__'" and I have no idea how to fix that. – John Kyle Mar 08 '16 at 21:11
  • Most OOP languages that I've seen use 'this', as sens4 mentioned above. Python has 'self. If you're used to programming in C++ or Java or other languages like those, you're probably used to seeing the type of the variable when you're passing it. In Python, you can't. Functions don't accept an int or a str; they just accept variables. Where are you seeing this error? What line? Are you sure that the variable that you're passing to it it a Point variable? – Blarghedy Mar 08 '16 at 21:13
1

your class accept point0 and point1 parameters when you call it. If you want to get values of these parameters you should use self.x(for point0) and self.y(for point1) or another way;

class Line:
   def __init__(self, point0, point1):
       self.point0 = point0
       self.point1 = point1

I suggest you to read;

Python __init__ and self what do they do?

https://www.ibiblio.org/swaroopch/byteofpython/read/class-init.html

https://docs.python.org/2/tutorial/classes.html

Community
  • 1
  • 1
FatmaT
  • 255
  • 1
  • 9
  • I did do that but for my _str_ method it keeps saying "AttributeError: Point instance has no attribute '__float__'" and I have no idea why it keeps saying that. – John Kyle Mar 08 '16 at 21:18
  • can you share the full traceback? – FatmaT Mar 08 '16 at 21:22
  • It was for the str method in my line class. The str method in my point class works fine but for the str in my line class it says return '%d %d' % (int(round(self.point0)), int(round(self.point1))) AttributeError: Point instance has no attribute '__float__' – John Kyle Mar 08 '16 at 21:26
  • it is not the reason. Here I tried ; In [7]: line = Line(3.2, 4.1) In [8]: line.__str__() Out[8]: '3 4' – FatmaT Mar 08 '16 at 21:30
  • I just added my test code. What it does is take a p0 and p1. p0 = Point(0.0, 1.0) and p1 = Point(2.0, 3.0) and then there's line = Line(p0,p1) and this generates the error about not being able to find a float value. While the error points to the str method, the problem could be in another method. – John Kyle Mar 08 '16 at 21:37
  • p0 and p1 are instance of Point class. you are not allow to use round for them.https://docs.python.org/3/library/functions.html#round. Instance of that you can use line=Line(p0.point0,p1.point1) – FatmaT Mar 08 '16 at 21:44
  • Still doesn't work. I still get the exact same error. I don't think the problem is in the test code. I think the test code is fine (probably because I didn't write it so there's no reason for it to be wrong). I really think the problem is within my line class (or maybe even my point class). – John Kyle Mar 08 '16 at 21:48
  • I just copied your codes and replace x and y with point0 and point1 in Line __init__ and here is the result. In [61]: p0 = Point(0.0, 1.0) In [62]: line = Line(p0.x, p0.y) In [63]: print line 0 1 In [64]: p1 = Point(2.0, 3.0) In [65]: line = Line(p1.x, p1.y) In [66]: print line 2 3 – FatmaT Mar 08 '16 at 21:54
  • @JohnKyle This is the correct answer for the error you reported in your question, but your code had quite a few other errors as well. See my answer for a working example. – chepner Mar 08 '16 at 23:50
0

I'm going to add another answer here, both because I lack the reputation to comment on the other answer and because I feel this answer is unrelated to my previous answer (which addressed a different problem than what you're seeing now).

So. That said, look at this line of code:

return '%d %d' % (int(round(self.point0)), int(round(self.point1)))

round is a function that takes a numeric argument. However, self.point0 and self.point1 are not numbers. They are points. If you want the numbers from them, you'll have to refer to those explicitly (i.e. self.point0.x).

DevLounge
  • 8,313
  • 3
  • 31
  • 44
Blarghedy
  • 47
  • 8
  • So are you saying that everytime I use self.point0 or self.point1 I have to instead say self.point0.x and self.point1.y? – John Kyle Mar 08 '16 at 21:56
  • So I changed all the point0 and 1's to point0.x and point1.y and that gets rid of the errors but the output isn't right. For example with rotate it returns the values 0 -1 but it should've returned 0 0 0 -1 – John Kyle Mar 08 '16 at 22:00
  • No. Your line variable has 2 points (point0 and point1). Every point has 2 values (x and y). You have 4 total values that you need to print, and you're only printing 2 of them. – Blarghedy Mar 08 '16 at 22:01
  • But how to I print out both values from point0 and 1? I'm using self.point0.x but I can't figure out how to add y in there. – John Kyle Mar 08 '16 at 22:07
  • I tried self.point0.x and self.point1.x and then the same with the y's but that doesn't work either. How can I make point0 and 1 point to all the values? I'm very confused about how this works. – John Kyle Mar 08 '16 at 22:22
0

A few overall points before displaying your corrected code. (Note that not much actually changed):

  1. Don't bother checking argument types. Python programmers are assumed to be responsible enough to read the documentation and pass values of the correct value.

  2. Your Line class was duplicating code that you had already defined in the Point class. The attributes of a line are Point instances, so you can use the methods you defined to implement the Line methods.

  3. There's no reason to round the point coordinates to integers when displaying them; show the actual floating-point values that define the point. Your Line.__str__ method can take advantage of the fact that you've defined Point.__str__.

And now, your much shorter and corrected code, with some interspersed comments.

import math


class Point:
    def __init__(self, x, y):
        '''x and y should be floats'''
        self.x = x
        self.y = y

    def rotate(self, a):
        '''Rotate the point around the origin by a radians'''
        self.x = math.cos(a) * self.x - math.sin(a) * self.y
        self.y = math.sin(a) * self.x + math.cos(a) * self.y
        # If you *were* going to check if a is a float, you
        # need to do it *before* you use it.

    def scale(self, f):
        '''Scale the point by f units'''  # you get the idea
        self.x = f * self.x
        self.y = f * self.y

    def translate(self, delta_x, delta_y):
        self.x = self.x + delta_x
        self.y = self.y + delta_y

    def __str__(self):
        # If you're storing floats, it's probably useful
        # to output them.
        return '(%f, %f)' % (self.x, self.y)


# Operations on a line all involve applying the same operations
# to each of its end points.
class Line:
    def __init__(self, point0, point1):
        self.point0 = point0
        self.point1 = point1

    def rotate(self, a):
        self.point0.rotate(a)
        self.point1.rotate(a)

    def scale(self, factor):
        self.point0.scale(factor)
        self.point1.scale(factor)
        # If for whatever reason you didn't want to use Point.scale
        # here, the code would be...
        # self.point0.x = f * self.point0.x
        # self.point0.y = f * self.point0.y
        # self.point1.x = f * self.point0.x
        # self.point1.y = f * self.point0.y

    def translate(self, delta_x, delta_y):
        self.point0.translate(delta_x, delta_y)
        self.point1.translate(delta_x, delta_y)

    def __str__(self):
        # You've already defined out to turn a Point into
        # a string, so you can take advantage of that here.
        return "%s -- %s" % (self.point0, self.point1)
chepner
  • 497,756
  • 71
  • 530
  • 681
  • Your code almost gets me the correct output however there's one more test case where I test for a shallow copy. p0.scale(2.0) p1.scale(2.0) print line. What I expect it to print out is 0 1 2 3 but it instead prints out 0 2 4 6. Why is that? – John Kyle Mar 09 '16 at 05:07
  • This question is already too broad; your comment is a new question altogether. – chepner Mar 09 '16 at 05:12
  • But I feel like it's a simple answer. It appears to be calling scale but I don't which one and I don't know why the values are multiples of 2 instead of 1. – John Kyle Mar 09 '16 at 05:16
  • It might be a simple answer, but the comment section is not the appropriate place to raise new question. Besides, there's not enough room in the comment to fully explain what the problem is. You say `print line`, but never explain how you set `line`. (And do not follow this comment with something like `line = Line(p0, p1)`; I'm not answering the question here.) – chepner Mar 09 '16 at 12:39