Login | Register
Nerd ParadiseArtisanal tutorials since 1999

If you already know Object Oriented concepts from another language but need a quick run-down of how Python's OO syntax works, then this post is for you. This post does NOT teach Object Oriented programming as a concept. This is for all of you out there that don't need an explanation of what an abstract class or static field are, and just want to cut to the chase of how to do that in Python.

Basic class definition with constructor

class Person:
  def __init__(self, name, age):
    self.name = name
    self.age = age

  def say_hello(self):
    print("I am " + self.name + " and I am " + self.age + " year(s) old")

...
p = Person("Billy"2.8)
p.say_hello() # I am Billy and I am 2.8 years old

The first anomalous things you might notice as compared to most languages is that the constructor has an odd name: __init__. Additionally, fields do not explicitly need to be declared. They are declared by assigning to them. self is the reference to the current object instance as opposed to this, and must be passed in as the first argument to every method on the class.

Instantiation of classes is done by calling the class name like a function. No new keyword is necessary.

Extending a class and calling the base constructor

Extending a class is done by placing the base class in parentheses after the class name in its definition, e.g. class Student(Person):.

Python 2 or 3: Calling the base constructor is done by explicitly calling the __init__ function for the base class and passing in a reference to self.

Python 2 only: You can also call a built-in function called super which takes in two arguments: the current class and the current reference and returns a reference to self, except any methods invoked on it will be on the parent class instead of the current class. This works a lot like super or base in other languages, except note that in Python it must be invoked like a function first. From that, you can call the parent class' __init__.

Python 3 only: You can call super().__init__(args) without passing anything into the super function the way you did in Python 2.

class ThingsThatHaveNames:
  def __init__(self, name):
    self.name = name

class Person(ThingsThatHaveNames):
  def __init__(self, name, age):
    self.age = age

    # Choose one of these lines depending on your situation:
    ThingsThatHaveNames.__init__(self, name) # Python 2 or 3
    super(Personself).__init__(name) # Python 2
    super().__init__(name) # Python 3 and recommended moving forward

Explicitly declaring fields

You CAN explicitly declare fields in Python, though. Assigning a value to a field that is not declared will cause an error. This is achieved by defining __slots__ as a list of strings in the following manner:

class Person:
  __slots__ = ['name''age']
  def __init__(self, name, age, height):
    self.name = name
    self.age = age
    self.height = height # <-- ERROR!!

Static fields

Static fields in Python are done in the form of variables declared in the first indention level inside the class.

class Person:
  population = 0
  def __init__(self, name, age):
    Person.population += 1
    self.name = name
    self.age

...

print("The world population is"Person.population)

Static methods

Static methods in Python look like regular methods, except they contain the @staticmethod decorator above them.

class Person:
  def __init__(self, name, age):
    self.name = name
    self.age = age

  @staticmethod
  def create_unknown_person():
    return Person("John Doe"30)

Note that the method does NOT have a self argument in it.

There are also things called @classmethods that work similarly. The difference between classmethod and staticmethod is that the classmethod will take in the class as the first argument, similar to how a regular method takes in an instance as the first argument. A class method is also inherited to subclasses whereas a staticmethod is only defined on the class that the definition appears in. If you come from a Java or C# background, you're probably used to the @staticmethod behavior as opposed to the @classmethod behavior.

Private and protected fields in Python

This cannot be done. Convention, though, dictates that one should use an underscore prefix in front of field and method names to make them protected and double underscores to make them private.

There are problems with this. An underscore prefix only SUGGESTS that it should be treated as private and in no way actually prevents access to the field/method. A double underscore is slightly stronger in that Python will actually change the name to make it harder to access, but the field is still accessible if you know what the name is (it's the class name, field name, and some underscores, or you can just look it up with dir() at runtime).

Closures, on the other hand, can give you a better implementation of private fields BUT THIS STILL DOESN'T WORK 100% (see my comment below the code).

class Person:
  def __init__(self, name, age, ssn):
    self.name = name
    self.age = age
    private_ssn = ssn
    self.get_ssn = lambda"XXX-XX-" + ssn[-4:]
    self.verify_ssn = lambda value: value == ssn

p = Person('Steve'46"123-45-6789")
print(p.get_ssn()) # last 4 digits only
print(p.verify_ssn("555-55-5555")) # False
print(dir(p)) # Nice try, but that won't work.

You can still get a reference to the raw closure object itself in this example by calling p.get_ssn.func_closure[0]. From this you can generate a new function that points to the same closure object (using a module called new) that simply returns the value.

Abstract classes

Python 3.4+ ONLY: An abstract class can be defined by extending from a class called ABC located in the abc module. This stands for "Abstract Base Class". Then place @abc.abstractmethod in front of all the abstract methods.

import abc

class Person(abc.ABC):
  def __init__(self, name, age):
    super().__init__()
    self.name = name
    self.age = age

  @abc.abstractmethod
  def do_job(self):
    pass

class Baker(Person):
  def __init__(self, name, age):
    super().__init__(name, age)

  def do_job(self):
    print("I bake the bread!")