Python self
is one of the source of confusion and questions when someone start learning Python, especially if they come from other languages such as Java, PHP, Ruby etc.
In all languages mentioned above, when you define a class, a reference to the class instance always created automatically. Take for example the following PHP code:-
<?php
class Person {
public $name = "Kamal";
public function info() {
echo $this->name;
}
}
We can see above $this
can be used to refer to the class instance. The same code in Python is like this:-
class Person:
name = "Kamal"
def info(self):
print(self.name)
So the code almost identical except for the difference in PHP the reference to class instance is called this
while in Python it is self
. And even self
is arbitrary and simply pure convention. We can name it as this
if we want.
The main difference between PHP and Python code above is that we have to specify the self
parameter as a first parameter to the method definition. Omitting it will give us error when trying to call the instance method:-
>>> class Person:
... name = "Kamal"
... def info():
... print(self.name)
...
>>> p = Person()
>>> p.info()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: info() takes 0 positional arguments but 1 was given
One common question is why we have to explicitly define the self
parameter? Why can't it be made implicit like in other language? One way to answer this is because the explicit self parameter allow us to specify different instance when calling the method. Take the following code:-
p = Person()
p.info()
Above code actually just a syntatic sugar over calling a function in certain class and specifying the class instance as the first argument. The above code can also be written this way:-
p = Person()
Person.info(p)
The following question could what the practical use case for this? I can't think anything specific on top of my head right now. Probably in situation when you want to call method on p
dynamically.
However, when looking through the code of pq recently, I noticed something interesting with how they define the task() function. Some snippet of the function:-
def task(
queue,
schedule_at=None,
expected_at=None,
max_retries=0,
retry_in='30s',
):
def decorator(f):
f._path = "%s.%s" % (f.__module__, f.__qualname__)
f._max_retries = max_retries
f._retry_in = retry_in
queue.handler_registry[f._path] = f
@wraps(f)
def wrapper(*args, **kwargs):
Just look at the function parameters and ignore the rest of the code. And this is how the docs show how to use it:-
from pq.tasks import PQ
pq = PQ(...)
queue = pq['default']
@queue.task(schedule_at='1h')
def eat(kind):
print 'umm, %s apples taste good.' % kind
eat('Cox')
queue.work()
So task()
is a method of PG
class? But we can see above that task() is defined as function? Turn out in line 94 the task function was added as attribute to class PQ
. That essentially make it a method of that class. How this can work? Let see how the task() function will be used as function:-
pq = PQ(....)
queue = pq["default"]
@pq.task(queue, schedule_at='1h')
def eat(kind):
print 'umm, %s apples taste good.' % kind
Notice that when calling task()
as function, we have to specify queue
as its first argument, since that what it expecting as per the function definition. And when calling it as method, Python will automatically pass the PQ instance as the first argument, also satisfying the function requirement.
This pattern allow us to offer our library API both as function and method.
Top comments (0)