Master object-oriented programming in Python with classes, inheritance, and more
Classes provide a way to bundle data and functionality together. Python’s class mechanism is powerful yet straightforward, supporting all standard features of object-oriented programming.
In Python, objects have individuality, and multiple names can be bound to the same object (aliasing):
a = [1, 2, 3]b = a # b is an alias for ab.append(4)print(a) # [1, 2, 3, 4]
This is important for mutable objects like lists and dictionaries. For immutable types (numbers, strings, tuples), aliasing doesn’t affect program behavior.
class Dog: kind = 'canine' # class variable shared by all instances def __init__(self, name): self.name = name # instance variable unique to each instanced = Dog('Fido')e = Dog('Buddy')print(d.kind) # 'canine' (shared)print(e.kind) # 'canine' (shared)print(d.name) # 'Fido' (unique)print(e.name) # 'Buddy' (unique)
Shared mutable objects can cause problems:
class Dog: tricks = [] # mistaken use of a class variable def __init__(self, name): self.name = name def add_trick(self, trick): self.tricks.append(trick)d = Dog('Fido')e = Dog('Buddy')d.add_trick('roll over')e.add_trick('play dead')print(d.tricks) # ['roll over', 'play dead'] - unexpectedly shared!
Correct design uses instance variables:
class Dog: def __init__(self, name): self.name = name self.tricks = [] # creates a new empty list for each dog def add_trick(self, trick): self.tricks.append(trick)d = Dog('Fido')e = Dog('Buddy')d.add_trick('roll over')e.add_trick('play dead')print(d.tricks) # ['roll over']print(e.tricks) # ['play dead']
class Base: def greet(self): return "Hello from Base"class Derived(Base): def greet(self): base_greeting = Base.greet(self) return f"{base_greeting} and Derived"d = Derived()print(d.greet()) # Hello from Base and Derived
Name mangling (__spam): Avoid name clashes in subclasses
class Mapping: def __init__(self, iterable): self.items_list = [] self.__update(iterable) def update(self, iterable): for item in iterable: self.items_list.append(item) __update = update # private copy of original update() methodclass MappingSubclass(Mapping): def update(self, keys, values): # provides new signature for update() # but does not break __init__() for item in zip(keys, values): self.items_list.append(item)
Name mangling replaces __spam with _classname__spam to avoid conflicts.
>>> sum(i*i for i in range(10)) # sum of squares285>>> xvec = [10, 20, 30]>>> yvec = [7, 5, 3]>>> sum(x*y for x,y in zip(xvec, yvec)) # dot product260>>> data = 'golf'>>> list(data[i] for i in range(len(data)-1, -1, -1))['f', 'l', 'o', 'g']