On Sun, Jan 19, 2014 at 04:59:58PM -0500, Christian Alexander wrote: > Hello Tutorians, > > Looked all over the net for class tutorials > Unable to understand the "self" argument > Attempting to visual classes
Let me explain classes by analogy. Classes are best as representing things, that is, nouns, whether abstract nouns like numbers, or concrete nouns like cars. So let's work through an example. Suppose I have a class Car. You might think of the class as a factory which creates cars. We call that factory a class, and the individual cars built by the factory "instances". (This analogy doesn't go far enough: there is a lot more to the relationship between instances and their class, but for now we'll start with this slightly inaccurate picture and then improve it later on.) So the first thing that we do is we need to decide what sort of car the factory will produce. I don't mean the brand name and model, I mean, what does it do? What parts does it need? Two doors or four? So let's make a basic car that does just two things: it starts the engine, and stops the engine: class Car: def start_engine(self): if self.gear not in ('Park', 'Neutral'): raise RuntimeError('cannot start engine') self.engine.start() def stop_engine(self): self.engine.stop() Not a very interesting car, but all things must start somewhere. Now, lets look at those methods, `start_engine` and `stop_engine`. They take a single argument, `self`. What does self represent? It represents the individual car you wish to operate. You might end up with fifty cars: car1 = Car() # create a new car instance car2 = Car() car3 = Car() ... Every one of those cars shares access to the same `start_engine` method. So when you call the method: car23.start_engine() how does the method know to start car23's engine, instead of car42 or car18? It knows because Python takes the bit before the dot (`car23`), and automatically passes it to the method as the `self` argument. Confused? Let's make a brief side-track, and talk about functions. Here's a function: def greet(name, greeting): print greeting, name If I call that function: greet("Fred", "Greetings and salutations") => prints "Greetings and salutations Fred" Python takes the first value, "Fred", and assigns it to the argument `name`, the second value "Greetings and salutations" and assigns it to the argument `greeting`. Then the function can access them as local variables inside the function. The same applies to methods, with just one additional bit of magic: when you call a method like this: instance.method(a, b, c) # say Python turns it into a function call: method(instance, a, b, c) [For advanced readers: to be precise, Python uses what is technically known as an *unbound method*, which it gets by looking inside the class of `instance`. To be even more precise, in Python 3, it no longer uses unbound methods but just regular functions. But the basic concept is the same.] So, back to our car: we've got a car that has two methods, which controls the *behaviour* of the car, but they won't do anything except raise an exception, because the car doesn't have any *state*. It has no engine to turn on or off, and it has no memory of what gear it is in. So let's fix that. Every individual car needs its own engine, and each car needs to remember it's own gear. The obvious time to add the engine is when we create the car. Python creates the car when you call the class name as if it were a function: my_car = Car() # Python creates the car instance Now of course, Python doesn't know what cars are supposed to include, so it just does the bare minimum, which basically is just to allocate a small amount of memory for the instance and a few other bits of behind-the-scenes book-keeping. The rest is up to you, and that's what the __init__ method is for. So we give the car an engine when we create the car, and set the gear at the same time: class Car: def __init__(self): self.engine = Engine() self.gear = "Neutral" def start_engine(self): if self.gear not in ('Park', 'Neutral'): raise RuntimeError('cannot start engine') self.engine.start() def stop_engine(self): self.engine.stop() Of course, somebody has to write the Engine class too, but I can't be expected to do everything. Let's just pretend it already exists. Now you have a class which defines cars. When you call: my_car = Car() Python automatically calls the __init__ method with the newly created car instance as `self`. The __init__ method then runs, giving the car an engine, and setting the gear to neutral. Now, you should be able to start and stop the car: my_car.start() my_car.stop() and provided the engine class knows how to start and stop, so will the car. So, in conclusion: - the point of the `__init__` method is that Python automatically calls that method when you create an instance, so that you can initialise the instance with whatever state it needs to work; - the point of the `self` argument is so that the method knows which instance it ought to operate on. Obviously what I have described here only scratches the surface of what classes do. I haven't described inheritance, and my Car class is so lacking in functionality that it is a joke. But I hope that it helps you understand __init__ and self. -- Steven _______________________________________________ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor