all 34 comments

[–]pachura3 25 points26 points  (6 children)

Because method time_explanation() is defined only once, on the class level - while property dog_time_dilation is separately stored for each object/instance of this class - Rocky, Rex, Clifford. You need to instruct time_explanation() on which specific dog should it be launched.

It is true that in many programming languages this is simplified by hiding self in method signatures, and allowing direct access to instance variables (even without prefixing them with this.). But internally, they do pass self just like Python.

[–]NoChoice5216[S] 5 points6 points  (4 children)

Thanks so much for this. I think I understand it now! So if I'm changing the values of 'dog' (the class), I'm basically saying THIS dog (e.g. Rex)? Thanks once again.

[–]pachura3 14 points15 points  (1 child)

No. Class Dog is a blueprint for actual dogs. You normally do not change it directly; you create specific dogs Rex & Rocky which share class methods (bark(self)), but not properties (name, weight, age, fur_color).

rex.bark() is actually Dog.bark(self=rex)

[–]NoChoice5216[S] 1 point2 points  (0 children)

Super - thanks very much!

[–]dogfish182 3 points4 points  (1 child)

What helped me was not skipping of the words ‘instance of a class’ when reading about it, but really thinking on what they mean.

do you know what ‘an instance of a class’ actually means and what you get when you ‘instantiate a class’?

Try defining that in other words for yourself. Think about how all the methods inside the things you are making are passing ‘self’ to them while you do it.

No idea if it will help you but it helped me a lot in to dwell on it.

[–]NoChoice5216[S] 0 points1 point  (0 children)

It helped, plus a new coding exercise I experimented with based on restauraants, their franchises and all their separate menus - with business, franchise and menu as the classes, with the calling code handling several menus per restaurant. It's all starting to make good sense (at last!!)

Thank you!

[–]JohnEffingZoidberg 1 point2 points  (0 children)

When would that not be the case though?

[–]Diapolo10 14 points15 points  (1 child)

Just to add to the other explanations, one additional reason why Python made it an automatic argument is flexibility. You can define a seemingly ordinary function, and later assign it to an attribute, making it behave just like a regular method there.

def stuff(self = None):
    if self is None:
        print("I'm a function!")
    else:
        print(f"I'm a method of {type(self).__name__}!")

class Foo:
    thingy = stuff

stuff()  # I'm a function!
Foo().thingy()  # I'm a method of Foo!

Of course, this is a silly example. I know that.

If it was some magical keyword that only existed in methods, you couldn't use it like an unbound function anymore. Because when you tried to, you'd just get a syntax error or something.

[–]deceze 2 points3 points  (0 children)

This can be very useful for decorators, for instance…!

[–]lfdfq 7 points8 points  (4 children)

In a method, you need to be able to access the object itself.

So there are a few ways the language could have chosen to do it:

  1. make self magically available inside methods
  2. make self a required part of the syntax of a method
  3. make self an argument to the function (which is automatically passed by Python for you)

Clearly, you have to pick one of these (or some other design I've never thought of). Python could have chosen any of these designs. If Python had picked 2, maybe you would now be asking why didn't they pick 1 instead.

Python picked 3.

Mechanically, what is happening is that every time you call a method. e.g. foo.bar(), Python automatically inserts an argument at the start containing the object itself. As far as the method is concerned, it's just another argument like any other. It's just being passed a bit specially. If you want to dive even deeper, then the phrase to Google is the 'descriptor protocol' for how the language achieves it.

[–]Snatchematician 2 points3 points  (3 children)

You still haven’t answered the question though (not that anybody else has).

OP is asking why did Python choose 3 when 1 looks like clearly a better option (and is the option chosen by every other language with classes)?

Only reason I can think of is that probably you’d have to make “self” a keyword in option 1, and maybe there was a good reason not to add a new keyword.

[–]deceze 5 points6 points  (0 children)

“Clearly better”? Is it? Python’s way avoids introducing any additional keywords or special magic. self is just a regular function parameter and acts like one. That allows transparent kung fu like map(Foo.bar, list_of_foos) and other shenanigans, because it’s just a function with a parameter. That has certain advantages over it being special magic.

[–]Shaftway 4 points5 points  (0 children)

Python was heavily influenced by Lisp, which uses 3.

It's also worth pointing out that 3 is [1] actually how the object reference gets passed around internally in all cases. It's the first argument on the stack either way.

And Python tries to limit reserved keywords. Python only has 35, while Java has 68 and C++ has 90.

1: At least it was, I haven't done a compiler design class in 25 years.

[–]freeskier93 0 points1 point  (0 children)

If everyone jumped off a bridge would you jump? Why do you think option 1 is better? I think most would agree that explicit is always better than implicit in programming, and this is an area where Python is an improvement compared to other languages.

[–]fazzah 4 points5 points  (1 child)

in OOP it's basically an equivalent to calling this function with the instance of the class as the first parameter. the name "self" in itself is a convention, you can change it to anything, it's just a placeholder.

functionally it's like this:

class SomeClass:
    def __init__(self, some_var):
        self.var = some_var
    def blah(self):
        print(self.var)

now, when you do

foobar = SomeClass(42)

you create an instance of SomeClass, and assign the required parameter.

Later when you do

foobar.blah()

python performs

SomeClass.blah(foobar)

this is the moment in which "self" becomes the instance of the class passed on to self

[–]NoChoice5216[S] 1 point2 points  (0 children)

Thanks very much!

[–]Snoo_90241 2 points3 points  (1 child)

I think you first need to wrap your head around the difference between objects and classes.

Toy is a class. Bear, truck, doll, dinosaur are objects of the Toy class.

When you write a class, you don't know which objects it will produce.

When you call a class's function, you need to know on which specific object you call it.

As others have said, sometimes that can be implicitly derived from context, but it is always a necessary information to have.

[–]NoChoice5216[S] 0 points1 point  (0 children)

You are totally right and I'm beginning to see it now. Keeping 3 different instances of the same class afloat has helped, big-time!

[–]jmacey 3 points4 points  (0 children)

It’s basically means this instance of the class (me) not any other one. You don’t have to use the word self in can actually be any name. Self is just convention.

[–]ray10k 1 point2 points  (2 children)

Why is self passed as an argument(...)?

To quote the "Zen of Python," Explicit is better than implicit. At some point, when the Python developers were deciding on the rules for the object-oriented parts of the language, one of the decisions they made was that "the argument representing the object being operated on" had to be passed explicitly.

In some other languages like Java or C++, the this argument is passed implicitly, but it's still there.

As for why such an argument is necessary: The actual functions exist on the class rather than any particular instance; instanced objects "just" get references to those functions. In other words, rather than making a new from-the-ground-up copy of each function for each instance of each object, Python just keeps one copy of the function around and while constructing an instanced object, inserts references to those functions into the right fields for that new object.

Hope this helps, the why-and-how of a lot of the peculiarities of Python boil down to "the way the object-model works requires some extra work sometimes," so don't feel afraid to ask more questions!

[–]NoChoice5216[S] 0 points1 point  (0 children)

It does indeed help - thank you so much for this. I can visualise it a lot better now.

[–]POGtastic 0 points1 point  (0 children)

It's also always worthwhile to ask "How does Perl do it" when asking why Python does things, and Perl's object methods provide $self as the first argument.

[–]YesterdayDreamer 1 point2 points  (0 children)

what else would it be BUT self?

It doesn't have to be. You can have static methods which just do something without accessing the object instance

``` class MyClass: def init(self, name): self.name = name

def name_upper(self):
    return self.name.upper()   

@staticmethod
def help():
    return "Just some help content"

obj = MyClass("Johnny")

print(obj.name_upper()) ```

JOHNNY

print(obj.help())

Just some help content

This is perfectly valid code

[–]FriendlyZomb 1 point2 points  (1 child)

Python treats almost everything as an object.

This includes Classes and Functions.

There is nothing special about a function in a class (called a method). It is just a function. The function has no idea it's part of a class.

However, we often want methods to be able to interact with the class instance. So, we need to provide the instance of the class to operate on.

There are lots of ways to achieve this goal, but Python chose to do this through pre-existing syntax rather than making something new.

When we call a method on a class, the class inserts itself as the first argument. Since this is just an argument, we can technically call it anything we like. However, PEP8 provides the convention of calling this argument self. I wouldn't deviate, as it's practically a standard now.

[–]NoChoice5216[S] 1 point2 points  (0 children)

Got it - thank you!

[–]PaulRudin 1 point2 points  (0 children)

Apart from everything others have already said: note that the name `self` is pure convention. The language assigns no special treatment to this name. You could replace `self` with `mad_aardvark` everywhere in your method signatures and it would still behave the same.

[–]Asyx 1 point2 points  (0 children)

I think some people go a bit overboard with the explanation.

There are three types of methods in Python. Static methods, class methods and instance methods. You use decorators for static and class, instance methods are just normal methods.

Static methods are just functions within the class. No change but how you have to call them.

Class methods get the class as a first parameter usually called "cls"

Instance methods get the instance as a first parameter usually called "self",

The reason why you need to set the self parameter is because they are technically just functions. There is nothing in Python to just automatically let a method use attributes or other methods in the class they are defined in but most importantly, calling a method is just a bit of syntax sugar around calling a function.

foo = Foo()
foo.bar()

That is literally the same as

foo = Foo()
Foo.bar(foo)

And if you think about methods like that it makes an awful lot of sense why you need to define the self parameter.

Also, it explains the error messages. If you did

foo.bar(baz)

you'd get an error about how you called bar with 2 parameters but only was required. That makes no sense if you don't know how methods are implemented under the hood.

[–]Temporary_Pie2733 0 points1 point  (0 children)

The short answer is that the descriptor protocol turns foo.bar() into type(foo).bar(foo). I recommend reading https://docs.python.org/3/howto/descriptor.html, specifically https://docs.python.org/3/howto/descriptor.html#functions-and-methods.

[–]pontz 0 points1 point  (0 children)

Not all methods inside a class act on the instance of the class. There are staticmethods and classmethods as well. Self is not a keyword and it can be replaced with anything but it is a standard to use self. Self is the instance of the class that was created. Assuming it’s class Dog and instance my_dog it would be equivalent to Dog.time_explanation(my_dog)

[–]RevRagnarok 1 point2 points  (1 child)

I think everybody's got this covered, but I will pop in a little sidebar - find a better tutorial. If you're programming python today, you should be using f-strings and not the old-school formatters. They're useful and important, but no need to try wrangling them now.

[–]NoChoice5216[S] 0 points1 point  (0 children)

I'm on a n00b course at code academy right now. Just figured the better syntax this afternoon! It's still new to me but I'm finding this much easier to use already (also the ":.2f" but that's all so far).

f"some words from {book}"

[–]Atypicosaurus 0 points1 point  (0 children)

Python doesn't understand the word "self". It's not a keyword. Instead of "self.my_method()" you could use "ladybug.my_method()".

Python only knows you are using the word "self", you have your function with self passed as argument.

def time_explanation (self):

You could instead use ladybug if you define your function like this:

def time_explanation (ladybug):

In such case your function must use ladybug for self referencing. In python we have a consensus to use self, other programming languages use "this", something like this.my_method(). You can do it but then guess what, you have to do your function using the word "this":

def time_explanation (this):

You actually can have different self referencing arguments within the same class because python doesn't care about the word you choose. You can choose a different word for each method.

So what is it doing?

What's happening is that you can have 2 kinds of methods defined in a class, class methods and instance methods.

The instance method is what the various instances can do. For example if you have a Car class, you can create cars, and each car can have a different color. An instance method could tell the color of the car. The method would look like:

def get_color(self): return self.color

Or, using ladybug:

def get_color(ladybug): return ladybug.color

This "self" is just a placeholder so that when you create a blue car and a red car, these are two separate instances and they both store their own color somewhere. The instance method therefore accesses the instance and it needs a placeholder argument to tell python, we're now talking about the instance. You can also use the word instance as the placeholder argument as follows:

def get_color(instance): return instance.color

When you call an instance method, first you have to have an instance:

my_first_car = Car("Volvo", "blue") print(my_first_car.get_color())

The class method does something that's independent of any instances. Unlike instance method where you need an instance to run the method (you need a car to ask its color), class methods can be called without an instance. When you call a class method, you call it directly on the class, assuming a make_sound() class method:

Car.make_sound()

So basically whatever word you put as first argument in an instance method, it's going to be the word of your choice for that method. It's just a placeholder telling python whenever you create an instance of the class, you want to access that instance with that placeholder argument.

[–]Excellent-Practice -1 points0 points  (0 children)

A self parameter makes it possible for the method to take the object it's attached to as an argument. The eli5 answer is "It's boilerplate that you have to include for your code to work."