How does a method know which object to change? In a Python instance method, the first parameter conventionally named `self` refers to the instance on which the method is called. Python supplies that argument automatically, so `task.complete()` needs no explicit `self` argument. section 8.2.1
Initialize and update one task
class Task: def __init__(self, title): self.title = title self.status = "created" def complete(self): self.status = "completed" task = Task("Laundry") task.complete() print(task.status) # completed Both methods use `self` to access the same task. Initialize attributes in `__init__` even if another method will update them later; otherwise, accessing an attribute before that method runs can raise `AttributeError`. section 8.2.1 section 8.1.3
`self` is a convention, not a Python keyword: another first-parameter name works, but `self` makes the method easier to recognize. If a method neither changes an instance nor relies on its data, reconsider whether it should be an instance method. section 8.1.1 section 8.2.1
class Task:
def __init__(self):
print(f"__init__ gets called, creating object at {id(self)}")
def __new__(cls):
new_task = object.__new__(cls)
print(f"__new__ gets called, creating object at {id(new_task)}")
return new_task
task = Task()
# output the following lines:
__new__ gets called, creating object at 140702458469952
__init__ gets called, creating object at 140702458469952
The matching object addresses trace construction: `__new__` creates the instance, then `__init__` receives it as `self`. section 8.1.1

