Python – Object Oriented Programming – Design Inheritance & Properties

Best Practices

Designing for Inheritance and Polymorphism




  • batch_withdraw() doesn’t need to check the object to know which withdraw() to call

Liskov substitution principle

Base class should be interchangeable with any of its subclasses without altering any properties of the program

  • Wherever BankAccout woorks –> CheckingAccount should work as well
  • Syntactically
    • function signatures are compatible –> arguments, returned values
  • Semantically
    • the state of the object and the program remains consistent
      • subclass method doesn’t strengthen input conditions
      • subclass method doesn’t weaken output conditions
      • no additional exceptions


Violating LSP

  • Syntactic incompatibility
    • BankAccount.withdraw() requires 1 parameter but CheckingAccount.withdraw() requieres 2
  • Subclass stengthening input conditions
    • BankAccount.withdraw() accepts any amount, but CheckingAccount.withdraw() assumes that the amount is limited
  • Subclass weakening output conditions
    • BankAccount.withdraw() can only leave a positive balance or cause an error,
    • CheckingAccount.withdraw() can leave balance negative
  • Chaning additional attributes in subclass’s method
  • Throwing additional exceptions in subclass’s method

NO LSP – No Inheritance




Managing Data Access – private attributes

All class data is public

  • any method or atttribute from a class can be accessed, by design

Restricting Access

  • naming conventions
  • use @property to customize access
  • Overriding __getattr__() and __setattr__() 

Naming convention – internal attributes

obj.att_name , obj._method_name()

  • starts with a single _ –> “internal”
  • widely accepted in Python community
  • Not a part of the public API
  • As a class user –> don’t mess with it
  • As a class developer –> use for implementation detail & helper functions, …
    • df._is_mixed_type –> indicator if df is of a mixed type
    • datetime._ymd2ord() –>  method for ordering dates

Naming convention – pseudoprivate attributes

obj.__attr_name, obj.__method_name()

  • starts but doesn’t end with __ –> “private’
  • not inherited
  • Name mangling: obj.__attr_name is interpreted as  obj._MyClass__attr_name
    • obj._MyClass__attr_name  =  actual name of the attribute
  • Use : prevent name clashes in inherited classes

!! Leading and trailing __ are only used for built-in Python methods –> __init__(), __repr__() !!

  • _name –> helper method that checks validity of an attribute value but isn’t considered a part of the class public interface
  • __name –> a ‘version’ attribute that stores the actual version of the class and shouldn’t be passed to child classes which have their own versions
  • __name__ –> a method run whenever the object is printed



Changing attribute values

Control attribute access ?

  • check the value for validity
  • make attributes read-only
    • modifying set_salary() doesn’t prevent emp.salary = -100
    • dot.syntax –> still access


Restricted and read-only attributes




  • Use protected attribute with leading _ to store data
  • Use @property on a method whose name is exactly the name of the restricted attribute – return the internal attribute
  • Use @attr.setter on a method attr() that will be called on obj.attr = value
    • value to assign passed as argument


Why use @property?

  • User-facing : behaving like attributes
  • Developer-facing : give control of access

Other possibilities

  • Do not add @attr.setter
    • Create a read-only property
  • Add @attr.getter
    • Use for method that is called when property’s value is retrieved
  • Add @attr.deleter
    • Use for the method that’s called when proporty is deleted using del