Close

Essential Object-Oriented Programming for a Python Interview

Essential Object-Oriented Programming for a Python Interview

This is a cheat sheet for object-oriented programming in Python. It provides examples for the common structures using Python classes. It covers all the items list in Essential Interview Code for Object-Oriented Programming.

If you are doing a coding interview in Python, it is recommended to know all of these items, or to keep this list nearby.

These examples are written with type annotations, which are optional, though recommended, in Python.

Classes

# Define a class
class User:
	def __init__( self, ref_id : int, name : str ):
		self.ref_id = ref_id
		self.name = name
		
# Instantiate a class
u = User( "QX843", "tom" )
print( u.ref_id, u.name )


# Release or cleanup an instance

# Define a class or static variable
class Counter:
	global_counter : int = 0
	
	def __init__( self ):
		self.counter = Counter.global_counter
		Counter.global_counter += 1

print( Counter().counter )
print( Counter().counter )
print( Counter().counter )
print( Counter.global_counter )
		
# Data structure
class UserRef(NamedTuple):
	ref_id : int
	name : str
	
u = UserRef( "QX748", "sally" )
print( u )

Visibility

Python has no formal public and private semantics. It uses a combination of leading single and double underscores to denote internal items, where single underscore is loosely equal to protected in meaning and double underscore private. Though unless you intend on having subclasses, single underscores are common.

# Public/Protected/Private fields
class Circle:
	def __init__( self, radius : float ):
		# public field
		self.shape = 'circle'
		# internal field
		self._radius = radius

	# Getter
	@property
	def radius( self ) -> float:
		return self._radius
		
	@property
	def diameter( self ) -> float:
		return self._radius * 2

	# Setter
	@radius.setter
	def radius( self, value : float ) -> None:
		self._radius = value
	
	
c = Circle(5)
print( c.radius )
print( c.diameter )

c.radius = 7
print( c.radius )

Virtual Functions

Member functions are by default virtual in Python.

from typing import *

class Encoder:
	def __init__( self ):
		pass

	# Define a virtual function
	def encode_link( self, text : str, href : str ) -> str:
		return None
		
	def get_format_str( self ) -> str:
		return "Standard"
		
	@final
	def get_output( self ) -> str:
		return 'some_file_name'
		

@final
class HtmlEncoder(Encoder):
	# Override a virtual function
	def encode_link( self, text : str, href : str ) -> str:
		return f'<a href="{href}">{text}</p>'
	
	def get_format_str( self ) -> str:
		# Call super class function
		return "HTML --" + super().get_format_str()

		
@final		
class MarkdownEncoder(Encoder):
	def encode_link( self, text : str, href : str ) -> str:
		return f'[{text}]({href})'

	def get_format_str( self ) -> str:
		return "Markdown --" + super().get_format_str()

def encode_doc( encoder : Encoder ):
	print( encoder.encode_link( "https://interview.codes/", "Interview.Codes" ) )
	print( encoder.get_format_str() )

	
encode_doc( HtmlEncoder() )
encode_doc( MarkdownEncoder() )

Interfaces

Python uses duck-typing and doesn’t require formal interfaces. However, it now offers a Protocol type which provides a clean way to specify interfaces.

from typing import *
from abc import *

# Define an interface
class Formatter(Protocol):
	@abstractmethod
	def number( self, value : int ) -> str:
		raise NotImplementedError()
		
	@abstractmethod
	def string( self, value : str ) -> str:
		raise NotImplementedError()
		
# Implement and interface
class TextFormatter(Formatter):
	def number( self, value : int ) -> str:
		return str(value)
		
	def string( self, value : str ) -> str:
		return value;
	
# Use an interface
def format( fmt : Formatter ):
	print( fmt.number( 123 ) )
	print( fmt.string( "Knock, knock!" ) )
	
format( TextFormatter() )

Abstract Classes

Abstract classes define an incomplete structure that a derived class needs to complete.

from typing import *
from abc import *


# Define an abstract class
class Incomplete(ABC):
	
	# Declare an abstract function
	@abstractmethod
	def implement_me( self ):
		raise NotImplementedError()
	
		
# Implement an abstract function
class Complete(Incomplete):
	def implement_me( self ):
		print( "hi" )
		
		
c = Complete()
c.implement_me()

Type Information

class Article:
	pass
	
class Fungible(Article):
	pass
	
	def only_in_fungible( self ):
		print( "Fungy" )
		
	
class Abstract(Article):
	pass
		
# Identify
f = Fungible()
print( type(f) )

# Compare
assert isinstance( f, Fungible )
assert isinstance( f, Article )
assert not isinstance( f, Abstract )

# Downcast
def downcast( a : Article ):
	# sufficient for runtime and for "mypy" to recognize a is a Fungible, there is no explicit downcasting in Python
	if isinstance( a, Fungible ):
		a.only_in_fungible()

	
def main() -> None:
	downcast(f)
	
main()

Edaqa Mortoray

An avid writer and expert programmer. He’s been on both sides of the interview table countless times and enjoys sharing his experiences. https://edaqa.com/