5

Can you please clarify about the perimeter variable in the below class.

I understand that the self.vertices is to a particular instance. Since perimeter is not defined with self, does that mean its a class variable here? Then is it not common to all the instances?

Is it not the right way to code the perimeter as self.perimeter, so its aptly declared to each instance?

This code is from a book.

Polygon.py

import math
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def distance(self, p2):
        return math.sqrt((self.x-p2.x)**2 + (self.y-p2.y)**2)

class Polygon:
    def __init__(self):
        self.vertices = []
    def add_point(self, point):
        self.vertices.append((point))
    def perimeter(self):
        perimeter = 0
        points = self.vertices + [self.vertices[0]]
        for i in range(len(self.vertices)):
            perimeter += points[i].distance(points[i+1])
        return perimeter
>>> square = Polygon() 
>>> square.add_point(Point(1,1)) 
>>> square.add_point(Point(1,2)) 
>>> square.add_point(Point(2,2)) 
>>> square.add_point(Point(2,1)) 
>>> square.perimeter() 
4.0 

New type

import math
class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def distance(self, p2):
        return math.sqrt((self.x-p2.x)**2 + (self.y-p2.y)**2)

class Polygon:
        def __init__(self):
            self.vertices = []
        def add_point(self, point):
            self.vertices.append((point))
        def perimetermethod(self):
            self.perimeter = 0
            points = self.vertices + [self.vertices[0]]
            for i in range(len(self.vertices)):
                self.perimeter += points[i].distance(points[i+1])
            return self.perimeter

if __name__=='__main__':
    p1 = Polygon()
    p1.add_point(Point(1,1))
    p1.add_point(Point(1,2))
    p1.add_point(Point(2,2))
    p1.add_point(Point(2,1))
    print(p1.perimetermethod())

3 Answers 3

12

Assigning a new variable with some_name = ... always creates the variable in the innermost enclosing scope (unless global or nonlocal are in play, but they're not relevant here). Assigning a new attribute name on an object creates the attribute on that object.

So self.foo = 1 assigns an attribute named foo on the object currently referred to by self. Conventionally, the name self is used as the first parameter to a method, which receives the object on which the method was invoked. So "defining a variable with self" isn't anything special; it's just the ordinary rules about assigning to attributes on existing object. Any attributes that exist in instance object itself obviously have to be specific to that instance.

perimeter = 0 inside the perimeter method of the Polygon class creates a variable in the innermost enclosing scope. That's the perimeter method, so it creates a local variable. A local variable only exists for the duration of the function call, so it's neither a class variable nor an instance variable. You can't access it from anywhere except within the scope of that particular method (and it has a new completely independent value on each invocation), so it can't be common to all the instances. But neither can you access a different value for it on each particular instance, so it's not an instance variable either.

If you had perimeter = 0 outside a method, in the class block itself, then the innermost enclosing scope would be the class block. That would create a "class variable", which is just an attribute on the class object. If an attribute is on a class, then obviously it can't be specific to any instance, because there's only one class but there can be any number of instances. As an aside, this is exactly what the __init__, add_point, and perimeter methods of the Polygon class are; they were assigned (with a def statement) in a class block, so they became attributes of the class object.


Summary:

  1. self.foo = 1 is assigning to an attribute on the object currently referenced by self (this is usually the "current instance")
  2. foo = 1 in a class block is creating a class attribute of the class being defined
  3. foo = 1 in a def block is creating a local variable of the function being defined

But you shouldn't really memorise it that way. They're just special cases of:

  1. Assigning to a dotted name like foo.bar.baz = 1 is writing to an attribute of an object
  2. Assigning to a simple name like foo = 1 is writing to a variable in the innermost enclosing scope
Sign up to request clarification or add additional context in comments.

5 Comments

THanks Ben..It was really great piece of explanation..One more question..Which would be correct way to calculate the perimeter..Have it as a instance variable(self.perimeter) or a function variable(perimeter)..Does it really matter?
In general, it depends on what your intentions are. In this case, it looks like you're using it as a temporary variable during the calculation of the return value of the method, so it would be odd for it to be an instance variable. Plus, it has the same name as a method, so it you were assigning it as an attribute of the object you wouldn't be able to invoke the method again after calling it once, because square.perimeter would then get the attribute perimeter instead of the method, and then calling it with square.perimeter() would give you an error.
@user1050619: To add some slightly stronger advice, yes local variable vs instance variable does matter. They do quite different things. If a local variable can fulfil your requirements, that's generally what you should use. Local variables are much easier to work with, because you only have to look at one function's code to figure out how it's used. An instance variable could be accessed from other methods, or even from outside the class entirely.
@user1050619 I would also expect an instance variable to be part of the "definition" of the instance; the data needed to pin down this particular instance. A perimeter doesn't sound like part of the definition of a particular polygon; instead it should be calculated from the information defining the polygon (the list of vertices).
Thanks much Ben..Sometimes asking dumb questions helps you clear all your doubts..:)
2

No, that means that it's local. Also, you don't want to use self.perimeter since it will shadow the method with the same name.

2 Comments

I have coded using the self.perimeter, what difference the above 2 types makes difference..which would be the right way?
@user1050619: Why would you put it on the object? What use does that serve, especially since you calculate it fresh each time?
0

In your code, there are two things called perimeter. One is a method on the Polygon class. The other is a local variable inside that method. There are no class attributes in your code.

2 Comments

I have coded using the self.perimeter, what difference the above 2 types makes difference..which would be the right way?
Having a method and a variable with the same name is just asking for trouble since it will be difficult for you (and the interpreter) to understand which you are referring to. I strongly suggest you change one of them.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.