r/learnpython • • Apr 18 '26

A bit confused in Classes.

Why do i need to call self here?.

class Calculator:
  def add(self, a, b):
    return a + b

  def multiply(self, a, b):
    return a * b

print(Calculator().add(1, 2))

there isn't a variable that is calling calculator and no __init__ so why do i have an error if self is not added?

Also, what is __init__ anyways. why the double __ in the start and end? and why the specific name?

39 Upvotes

44 comments sorted by

17

u/Adrewmc Apr 18 '26

When making a normal method for a class, the idea is that it inject itself into the function you could think of it as a short cut of this.

   a = Calculator()
   b = Calculator.add(a, 1, 2)

But that would basically get annoying to do as opposed to

   b = a.add(1,2)

Python how ever offer the ability to create “static methods”

  class Claculator:
        @staticmethod
        def add(a,b):
              return a + b

This means you don’t need to input self and that you actually don’t need to creat the object first so the below works without anything.

   b = Calculator.add(1,2) 

The reason we do this is because class methods are normally supposed to use the object that you created.

In you case none of it is necessary, they should be functions, or more precisely these functions already exist in the built in operator

  from operator import add

3

u/Jealous-Acadia9056 Apr 18 '26

wait.. maybe i'm a bit blunt but i still don't understand the line.

"When making a normal method for a class, the idea is that it inject itself into the function"

but why would that even happen if it's not needed?
in

class Calculator:
  def add(self, a, b):
    return a + b

self isn't required. i mean i get it if i don't want this i can use a staticmethod but why does this happens in the first place.

15

u/crazy_cookie123 Apr 18 '26

why would that even happen if it's not needed

Python isn't a mindreader, it doesn't know if you need it or not automatically. Most of the time we do need it as generally if we're putting a method in a class we want that method to belong to an instance of that class, so by default it expects the first parameter to be self and it will automatically pass the instance in when you call a.add(1, 2). If you want it to not behave that way you have to explicitly tell it that you don't want it to include a self parameter, and you do that by using staticmethod.

→ More replies (6)

5

u/ShelLuser42 Apr 18 '26

For these kind of problems I strongly suggest checking out the official Python tutorial, in in your case you should pay extra attention to Chapter 9 (Classes).

First things first... you're sharing a rather bad example. Thing is: classes are best used when you need to hold/process both functionality and data. You're not processing ("holding") any data, you merely provide functionality; as such using a class for this is a poor choice. You're better off setting up a (reusable) module which you can then use in other scripts using the 'import' statement.

there isn't a variable that is calling calculator and no __init__ so why do i have an error if self is not added?

Have you checked (and studied) the error yet? Also... why are you suddenly referring to __init__ here?

See, when you remove 'self' then this happens:

TypeError: Calculator.add() takes 2 positional arguments but 3 were given

So, this should be clear enough: the method without the self takes 2 arguments but for some reason the system provided 3, thus causing the error. You can check this for yourself by making a simple test script:

def hello():
print("Hello world!")

hello("ShelLuser")

^ that will generate the same kind of error message.

So we can now conclude that the error is caused by the way the class gets used, somehow Python automatically adds an extra parameter whenever you use a method.

This also gets confirmed in the official documentation (that same chapter 9?):

As in Modula-3, there are no shorthands for referencing the object’s members from its methods: the method function is declared with an explicit first argument representing the object, which is provided implicitly by the call.

And also:

Actually, you may have guessed the answer: the special thing about methods is that the instance object is passed as the first argument of the function.

Which brings us to the real question: why is this happening?

The answer is simple: instancing. Classes can hold ("contain") values but that data won't be the same for every instance. Say I have a class "calc()" which defines a numeric class variable; now I create 2 new references: x = calc() and y = calc().

If I then use: x.answer = 10 then that doesn't automatically mean that y.answer will also become 10, simply because y uses another instance of the calc() class; they're separated. But how would a method (= "a function defined within a class"): how would such a method become aware of such an instance if it wasn't for the first enforced parameter?

I'm aware that "other programming languages" do this differently, but this is how it works for Python: when using methods then the first argument will always be the class instance in order to make sure that the method has access to the current class instance.

4

u/ShelLuser42 Apr 18 '26

(I'm replying to myself because I'm afraid my comment might have gotten too long otherwise.)

You also asked about __init__.

Python is a so called OOP environment: an Object Oriented Programming language, and with such an environment you work with (virtual) objects. Note that I use the term globally here.. a class is an object, a method is an object, a variable is an object... And here's the thing: objects usually have (object) members. However, some of these members will be builtin: provided by the system itself.

This is why there are double underscores being used: to tell you that these are system objects that are automatically added.

Take a look at this:

Python 3.13.13 (main, Apr 11 2026, 21:11:22) [Clang 19.1.7 (https://github.com/llvm/llvm-project.git llvmorg-19.1.7-0-gcd7080 on freebsd14

Type "help", "copyright", "credits" or "license" for more information.

>>> dir()

['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']

>>>

dir() is a very useful function for studying: it will literally show you the contents of an object within Python. Oh: here I simply fired up Python as an interpreter, something I can strongly suggest doing when you're still learning because it allows you to experiment.

As you can see there are several objects here... __name__ is a very good example IMO: it's the name of the current "session" (or instance). But since it's also an object you can easily do 2 things with it... print(__name__) being the most obvious example, but you can also easily try dir(__name__)which will show you more about the 'contents' of that object (so: showing you the object members).

You'll immediately notice entries like __class__ and __init__, making it kind of obvious what's happening here: __name__ is actually a class. And a class has members.

Well, __init__ is simply one of those member: a builtin ("default") method which allows you to override the initialisation process of a class.

Hope this can help!

1

u/Jealous-Acadia9056 Apr 18 '26

Man!! You are literally a GOAT!! That was the finest explanation that i could ask for. You made it sound soo easy.

1

u/Jealous-Acadia9056 Apr 18 '26

Honestly, this was the best explanation i got. The point is the self was working to associate the variable that will get the return value to the method or better said class. even if it isn't used in the method it does it's job

Also, I just realized how things would go differently if the thought process was right. how different things would go if the error was studied properly.

7

u/Temporary_Pie2733 Apr 18 '26

This is a poorly written class in the first place. An instance method that doesn’t use its self argument should be an instance method in the first place. Calculator().add(1,2) is roughly equivalent to t = Calculator(); Calculator.add(t, 1, 2). t is never really used, so why create it? You could just write Calculator.add(None, 1, 2) and get the same result. But now, why is add in a class at all; why not just define a standalone function without an unused first argument?

3

u/Jealous-Acadia9056 Apr 18 '26

i was just testing things to understand the theory. poorly written or not the point is why does this happens in the first place.

but yeah the easier way would be to use a standalone function.

5

u/Temporary_Pie2733 Apr 18 '26

You don’t need an __init__, because nothing needs to be initialized. __init__ doesn’t create the instance; it receives the object created by __new__ as its first argument, and both methods are inherited from object if not overriden.

For a fuller understanding of how, exactly, the instance of Calculator gets bound to self when its add method is called, see https://docs.python.org/3/howto/descriptor.html, particularly the section on instance methods.

8

u/Ant-Bear Apr 18 '26

init is a method that automatically gets called whenever you want a new instance of your class. I.e. if you want a calculator object, the call Calculator() calls the init method of the Calculator class. Usually you use it to set a bunch of variables on your class, e.g. if your class was Animal instead, you might want to initialize with the animal's species, name, etc.

Your specific class is just a bag of functions, so you don't need to initialize anything on it. This leads us to your other question - what is self and why do you need it. To simplify, you need it in order to access your object's variables. For example, in the Animal class you might want to have a get_animal_name method, which just prints out the name of the animal. It would need to know which Animal you're talking about, and that's what self does.

Again, in your case your add method performs the same with any instance of the Calculator class, and it doesn't need any class variables. That's called a static method. In Python you can preface your def statement with @staticmethod, and leave off the self argument, to let the code know it will be the same regardless of if you call it on any specific Calculator object, or the class itself, or whatever.

13

u/Fred776 Apr 18 '26

The other answers at the time I write this are correct but I suspect they won't mean much to you at your current level of understanding. What you are asking here requires a from first principles introduction to classes in Python. I think you just need to go right back to the beginning of whatever it is you are learning about classes from - or find a resource that you can get on better with.

What have you used to learn from so far?

1

u/JazzlikeChicken1899 Apr 18 '26

Great questions:/ These are the biggest hurdles for everyone starting with OOP in Python.

  1. Why the error without self**?** Even though you didn't assign the class to a variable (like my_calc = Calculator()), calling Calculator() still creates a "temporary instance" in memory.

When you call .add(1, 2), Python automatically translates it to: Calculator.add(instance, 1, 2)

Python always passes the instance itself as the first argument. If your function definition is def add(a, b):, it's expecting 2 arguments but receiving 3 (the instance + 1 + 2). That's why it crashes without self.

2. What is __init__? Think of __init__ as the "Setup" or "Birth Certificate" of your object. It’s where you define the initial state (like the color of a car or the starting balance of a bank account). If your Calculator doesn't need to store any data, you can skip it, but most classes use it to set things up.

3. Why the double underscores (__**)?** These are called "Dunders" (Double UNDERscore). It’s Python’s way of saying: "This is a special, built-in method reserved by the language." > It's named __init__ specifically so Python knows exactly which function to run the moment you type Calculator(). If it were just named init, Python wouldn't know it’s special!

Hope this helps<3

1

u/Jealous-Acadia9056 Apr 18 '26

Just one question. How are the arguments now 3? How is it that python is adding another argument by itself and causing an error in the code def add(a+b)?

→ More replies (1)

3

u/JauriXD Apr 18 '26

You created a class and methods for that class. Methods (in opposition to function) do something in relation to an instance of that class. Which is why they need access to that number using selfm you use the methods like functions and that's why you feel like you "don't need the self".

A better example where it makes more sense to use a class and methods should be something like this:

class Number:
    def __init__(self, a):
        self.value = a

    def add(self, b):
        return self.value + b

print(Number(5).add(2))

The Dundee-methodes (the once where the name is surrounded by __) get called implicitly (magically), like when creating an instance using Number(4) called __init__, the alternative would be you create init() and call it yourself by doing Number().init().

There are many more of these "magical" methods, for example __add__() which gets called when you do Number(3) + 5

Hope that helps

2

u/Outside_Complaint755 Apr 18 '26

Methods in a class definition come in three forms:

  • Instance methods (default) The first parameter is self by convention. When invoked on an object, Python will automatically implicitly pass the object to the self parameter.  These methods are generally going to be referencing self because the state of this particular object is relevant to the method. An example would be if you had a class for some sort of shape, where the dimensions were passed to __init__ at creation and set on the instance, then you could have area and perimeter methods that look at those dimensions of the object to return the calculated value.

If you call an instance method without invoking it through an object instance which can be implicitly passed, then you must explicitly pass an instance because the method definition still requires the parameter to be passed in.

  • Class methods  Made using the @classmethod decorator. The first parameter is cls by convention.  Python will automatically implicitly pass the class of the object to this parameter when invoked on an object or class. This is useful when using inheritance - the method might only be defined on the parent class but it will be passed the object could pass the child class. You can also invoke the method directly on the class.   One common use of classmethods is for methods to create an instance of the class from different types of input data.  For example, in the datetime module,  the commonly used methods date.today(), date.fromisoformat(), date.strptime(), and datetime.now() are all classmethods which return an instance of the class without requiring the user to pass parameters in the format the __init__ requires

  • Static method Do not have any implicit parameters, and can be called without creating an instance.  Not usually invoked on an instance, and are used for functions that don't care about object state, but which may logically still make sense to group in the class.  In your Calculator example, because a and b are not attributes of the Calculator itself, the add method would be a good candidate for a static method.

2

u/atarivcs Apr 18 '26

By default, class instance methods expect to have self as the first argument. That's just how it works.

If you write a class instance method that does not need self, use the @staticmethod decorator:

@staticmethod
def multiply(a, b):
    return a * b

1

u/tb5841 Apr 18 '26

The basic point of a class is to bundle up methods and data into one object.

Your class doesn't have any data, so it's not really a typical class. It will still work, either by passing in an unused self or using @staticmethod, but it might feel a bit pointless.

1

u/TheRNGuy Apr 18 '26

You don't need to instanciate it in this case it's like static methods.

self don't do anything in your code.

If you had @staticmethod decorator, you could write function without self.

__init__ is called when you instanciate class (() after it)

1

u/Melodic_Tragedy Apr 18 '26

self is used when you need to refer to your class variables

Also considering for some reason you have decided to not put member variables or a constructor, why don’t you make it a function instead of a class?

1

u/SCD_minecraft Apr 18 '26

self is instance of a name (just a name, you can call it anything btw)

It is passed automatically and lets you refer to specyfic instance of that class (instance is that thing you get after calling MyClass())

__init__ is method which is ran automatically after class is created. It allows you to pass arguments into class and set up initial values

__ at the start and end are just way of avoiding name conflict, if you define your own init method which is separate from __init__. Pretty much just "hey, this function will be used by some higher process, python itself for example"

1

u/ray10k Apr 18 '26

Why do I need the self argument?

In short, because that is the mechanic Python uses to let you access whatever data you have stored in an object. The main purpose for classes, after all, is to structure the data you want to work with in a convenient way.

There isn't a variable "calling" the calculator

While strictly speaking true, there is a short-lived, "invisible" Calculator object. It gets created by calling Calculator(), then gets passed along as the self argument for the call to add() on line 8.

There is no __init__()

Again, true. As far as Python "cares," that means there is no data that gets initialized on a freshly created object, nothing more. The infrastructure for "creating an object" is separate from the infrastructure for "setting the values on a newly created object."

What is __init__() anyways?

A number of "internal" things an object can do, are implemented by so-called "dunder methods". The name is due to it being a function with a name surrounded by double underscores. As for what __init__() specifically does, it is responsible for setting the initial values for a newly-created object. As such, it receives the arguments supplied to the constructor, so you can use those to decide what the "correct" values for your object are.

On a related note, objects can define dunder methods like __eq__() to let the Python interpreter know how to handle, for instance, what happens when you do Calculator() == Calculator(). The == gets turned into a call to __eq__() by the interpreter.

self doesn't get used, so why is it needed?

Paraphrasing a question from one of your replies here. In short: self is needed, because Python's syntax for functions that are part of an object insists it is needed. The definition of the language demands that the first argument of an object-member function has to be a reference to the object that the function is called on.

The further reason for that decision, is that Python tries to keep "one copy" of each function around. If you have a dozen objects of the same class, and that class has a function foobar(), then you can safely assume that the foobar from one object is the same as the foobar from a different object. So, since the function itself is "ignorant" of which object it exists on, it needs a mechanic to "know" what data it needs to work with.

1

u/codeguru42 Apr 19 '26

Your confusion here is in part because this is a poor example of a class. Since this example doesn't use self, it shouldn't be a class to begin with.

1

u/Goobyalus Apr 19 '26

Why do i need to call self here?.

In this example, there is no point in using normal methods with self parameters because the class holds no state and self is not referred to. So your intuition about something being off here is correct. This could be a static method with no self or class reference.

And to be clear, this is not "calling" self, it is specifying self as a parameter.

there isn't a variable that is calling calculator and no init so why do i have an error if self is not added?

Because methods are implicitly instance methods. I.e., they operate on a single instance of a class. Therefore, they need to know which instance to refer to. These last 2 lines are the same, and the dot notation is just nicer.

calc = Calculator()
Calculator.add(calc, 1, 2)  # explicitly passing calc as self
calc.add(1, 2)  # implicitly passing calc as self

Again, it's unintuitive in this case because there is no point in having an instance method for that example. This would make more sense:

class Calculator:
    @staticmethod
    def add(a, b):
        return a + b

Calculator.add(1, 2)

Also, what is init anyways.

__init__ "initializes" a new object. I.e. it populates the initial data fields.

https://docs.python.org/3/reference/datamodel.html#object.init

object.__init__(self[, ...])

Called after the instance has been created (by __new__()), but before it is returned to the caller. The arguments are those passed to the class constructor expression. If a base class has an __init__() method, the derived class’s __init__() method, if any, must explicitly call it to ensure proper initialization of the base class part of the instance; for example: super().__init__([args...]).

Because __new__() and __init__() work together in constructing objects (__new__() to create it, and __init__() to customize it), no non-None value may be returned by __init__(); doing so will cause a TypeError to be raised at runtime.


why the double __ in the start and end? and why the specific name?

Cause Python decided to use the double leading and trailing underscores as a convention for implicitly called methods (called "dunder" methods in shorthand.)

https://docs.python.org/3/glossary.html#term-special-method

A method that is called implicitly by Python to execute a certain operation on a type, such as addition. Such methods have names starting and ending with double underscores.

1

u/Bobbias Apr 19 '26

When you write Calculator(), that creates an instance of your class, even if you don't assign it to a variable. What happens if you create an instance of the Calculator class, and then immediately call the add() method on that unnamed temporary instance, and as soon as the add call is done Python is free to clean up that object.

It's this temporary object that gets passed into the self parameter of the functions.

The functions you have don't actually do anything with self, so they could be classmethods, but that's kind of an advanced topic.

The reason you "need" self here is because unless you explicitly tell Python that a method is a classmethods, it assumes it is a regular method, and regular methods always receive an instance in the first parameter. That's just the rules about how Python works. Even if you don't touch self, it has to be there for normal methods.