FAQ: Learn Python: Classes - Methods

This community-built FAQ covers the “Methods” exercise from the lesson “Learn Python: Classes”.

Paths and Courses
This exercise can be found in the following Codecademy content:

Computer Science

FAQs on the exercise Methods

Join the Discussion. Help a fellow learner on their journey.

Ask or answer a question about this exercise by clicking reply (reply) below!

Agree with a comment or answer? Like (like) to up-vote the contribution!

Need broader help or resources? Head here.

Looking for motivation to keep learning? Join our wider discussions.

Learn more about how to use this guide.

Found a bug? Report it!

Have a question about your account or billing? Reach out to our customer support team!

None of the above? Find out where to ask other questions here!

I am having a problem with the compiler with this exercise. The very first step gives me the error: “SyntaxError: unexpected EOF while parsing” with the code:

class Rules:

I checked and its the exact same as the solution given by codecademy (not much difficulty in this very first step…)

Any ideas on why?
THanks!

i had the same problem.
i think the reason why is because just wrighting the class is not the right proper syntaxt.
an class alawys needs to be defined. if you want to define an empty class you should use.

class Rules:
pass

and then later when you continue the code you can get rid of pass that is how i did it

i dont understand why in the example is:
class Dog():
dog_time_dilation = 7

and in the excersise is:
class Rules:

with out (), whats the difference?, i was using parentesis, as class Rules(): and i ve got the error metioned : “SyntaxError: unexpected EOF while parsing”

All the Python 3 documentation I’ve read does not include parens on class definitions, although Python 2 used to include class Foo(object) and both require parens on derived classes for obvious reasons.

class Rules:
    pass

We must include at least pass for the syntax to be complete at this stage. Just a signature line is not enough and will raise a syntax error. The Shell will not let us return to the command prompt if we haven’t added the pass line.

Bottom line, until someone pipes in with better information, it doesn’t seem to make any difference. The convention is to not use them, so that would be the preferable option, as I see it.

2 Likes

thanks for the answer, i was confused on when to use them, :slight_smile:

i think the parentesis are the error, maybe the example of codeacademy is incorrect??

1 Like

I cannot produce any errors with or without the parens on the definition line.Going forward it might be best to not use them.

1 Like

I am struggling to understand the example given.

class Dog():
  dog_time_dilation = 7

  def time_explanation(self):
    print("Dogs experience {} years for every 1 human year.".format(self.dog_time_dilation))

pipi_pitbull = Dog()
pipi_pitbull.time_explanation()
# Prints "Dogs experience 7 years for every 1 human year."

Notice we didn’t pass any arguments when we called .time_explanation() , but were able to refer to self in the function body. When you call a method it automatically passes the object calling the method as the first argument

I do not understand the role of self in this instance. Does the code self.dog_time_dilation call the number seven as a class variable for dog? Is self part of the dog class?

Also, is pipi_pitbull the object calling the method?

TIA

This is an issue which has come up several times recently: how should you call a class variable?

In this specific case, self.dog_time_dilation takes the class variable 7 and prints it. The method would work the same if the expression were Dog.dog_time_dilation. It does not make any practical difference, since the number is printed, and not saved as an attribute of the calling object.

Yes, pipi_pitbull is the object calling the method.


Let’s add some methods and a new class variable:

class Dog():
    dog_time_dilation = 7
    number_of_dogs = 0
    def __init__(self):
        Dog.number_of_dogs += 1
        self.dog_time_dilation = Dog.dog_time_dilation

    def time_explanation(self):        
        print("Dogs experience {} years for every 1 human year.".format(self.dog_time_dilation))

    def change_multiplier(self, x):
        self.dog_time_dilation += x  # changes the value only for the calling object

OK, now call:


pipi_pitbull = Dog()
pipi_pitbull.time_explanation()
pipi_pitbull.change_multiplier(3)  # changes for pipi only
pipi_pitbull.time_explanation()
bobby_boxer = Dog()
bobby_boxer.time_explanation()  # original class variable
print(Dog.number_of_dogs)  # class variable changes with each new dog

Output:

Dogs experience 7 years for every 1 human year.  # 1st call for pipi
Dogs experience 10 years for every 1 human year. # 2d call for pipi
Dogs experience 7 years for every 1 human year. # 1st call for bobby
2

If you look at the above, you can see that if I call a class variable using self (as in change_multiplier() method), the change affects only the calling object (pipi). But if I change it using the class name (as in number_of_dogs), it is changed for all.


So, it depends upon what you are using the class variable for. If it is a constant, applicable for all objects, and never to be changed, I’d use ClassName.var (and probably name the variable in all caps.) If it is a sort of starter or initial value for each object, but which may change in a different way for each object, best to use self.var. And if it is a class variable offering information about the class, such as number of members, obviously use ClassName.var.

What is happening under the hood to lead to very different outputs when calling a method with the parentheses in different places?

In other words, why does a return the string and d a TypeError?

class Rules:
  def washing_brushes(self):
    return "Point bristles towards the basin while washing your brushes."

a = Rules().washing_brushes()
b = Rules.washing_brushes
c = Rules().washing_brushes 
d = Rules.washing_brushes()

Where a, b, c, and d return, respectively:

Point bristles towards the basin while washing your brushes.
<function Rules.washing_brushes at 0x7f0b76032d08>
<bound method Rules.washing_brushes of <__main__.Rules object at 0x7f0b76082f98>>
TypeError: washing_brushes() missing 1 required positional argument: 'self'

That’s not very under-the-hood though is it?

An expression describes a value

so for example the expression

1

evaluates to 1

similarly, a function

f

evaluates to … f

What would you do with a function? Call it, presumably. You now have a different expression, you did something with f

f()

The result is whatever f returns, and f probably returned something other than itself (though it could, nothing stopping it from doing so)

So when you’re deciding whether or not to call something … did you want the thing itself, or its return value?

The dots in your expressions aren’t any different from your parenthesis, you could omit those as well, they too, are operations on values

x.something

asks x for its something attribute

x

doesn’t, that’s x itself

so, if you have some value, then, what operations does that support, and which operations do you want to do to it? you wouldn’t pick operations at random would you? or if you do, don’t expect a particularly meaningful outcome

you’ve got a class.
what would you like to do with it?
usually what you’d do with a class is to create an instance of it. for example:

an_empty_list = list()

calling a class results in an instance of it.

lists_class = list

so now you’ve got two values, list() and list … those are really different things.
you could ask for attributes from them

list().something
list.something

what attribute does a list have? what attributes does the list class have?

what makes sense to do with the list class’s append attribute, and what about a list instance’s append attribute?

if you call a list’s append attribute what’ll happen is that the value you provide is added to that list

but if you do the same with the list class, then what list would this add to? it wouldn’t, there’s no list, only the idea of list, a type

If you have a function that returns a list:

def f():
    return []

then f.append doesn’t seem like a great idea, f is a function, it doesn not have an append attribute
but you could call f, you would then have a list: f() and if you have a list then you could ask that list for its append attribute: f().append and if you had something you wanted to append to that list then… you could call it f().append(f)

f().append
f.append

Okay, I think I understand:

  • a returns the string because I have called (created an instance of) Rules, then called washing_brushes on Rules, which returns whatever the washing_brushes method within the class returns.

  • b returns Rules.washing_brushes itself because I haven’t called anything.

  • c returns something which is basically telling me there is a method within the instance of Rules named washing_brushes.

  • d throws a TypeError because I have tried to call a method on Rules itself. Because there is no instance of Rules in the expression, nothing is passed into washing_brushesself parameter.

you and I already did this.

functools.partial

stuff = list()  # []
bound_append = partial(list.append, stuff)  # stuff.append
bound_append(1)
print(stuff)  # [1]

And since I made the comparison between partial and method binding, I may as well show that functions can be used in a similar manner to classes - to create instances with fields and methods

Classes do this a bit more efficiently and provide nice things like iteration, indexing, calling, string conversion, operators … and you get a type for the value

from collections import namedtuple


def Counter():
    begin = 0

    def increment():
        nonlocal begin
        begin += 1

    def read():
        return begin

    # namedtuple provides attributes so that seems like an appropriate
    # representation, could be any container, including a function
    return namedtuple("Counter", ["increment", "read"])(increment, read)


c = Counter()
print(c.read())  # 0
c.increment()
c.increment()
c.increment()
print(c.read())  # 3


def Counter():
    begin = 0

    def increment():
        nonlocal begin
        begin += 1

    def read():
        return begin

    def get(msg):
        if msg == "read":
            return read
        if msg == "increment":
            return increment

    # just to show that a function can serve as a container/interface
    return get


c = Counter()
print(c("read")())  # 0
c("increment")()
c("increment")()
c("increment")()
print(c("read")())  # 3

One could also create the methods outside the function and bind them using partial similarly to a class… but those methods would no longer see the function scope, so one might want to use a container to store all the fields and provide this when binding a method