NB: More About Classes

Programming for Data Science

Some Useful Facts about Python Classes

You can put your classes in a .py file and import them into your other scripts.

You can put more than one class in a file.

Class names are usually written in CamelCase, but not instances.

When saving to a file:

  • If only one class, use a lower case name of the class in the file name.
  • If more than one, use a package name, one that stands for the logical group the classes belong to.

Anatomy of a Python Class

Let’s look a little more closely at the contents of a Python class.

# Import class dependencies here, outside of the class definition
import pandas as pd
import numpy as np

# Use CamelCase to name your class
class MyClass(object): # Optionally pass ancestor classes to use inheritance
    """
    Extensive docstring describing structure and function of class.
    """
    
    # Class attributes go here with initial values if applicable
    att1 = True 
    att2 = 'Foo'
    _private_att = 'Bar' # A variable that is meant to be internally only; not intended to accessed by users
    
    # Object initializer; called when an instance of the class is created
    def __init__(self):
        pass
    
    # Methods that share state via the self variable
    def get_something(self):
        pass
    
    # Name your methods consistently -- prefix with verbs
    def put_something(self):
        pass
    
    def do_something(self):
        pass

    def show_something(self):
        pass

    # A private method
    def _private_method(self): 
        pass
    
    def do_something_else(self):
        # Use private method
        self._private_method()
    
    # A static method -- no self
    def static_method():
        pass
help(MyClass)
Help on class MyClass in module __main__:

class MyClass(builtins.object)
 |  Extensive docstring describing structure and function of class.
 |  
 |  Methods defined here:
 |  
 |  __init__(self)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |  
 |  do_something(self)
 |  
 |  do_something_else(self)
 |  
 |  get_something(self)
 |      # Methods that share state via the self variable
 |  
 |  put_something(self)
 |      # Name your methods consistently -- prefix with verbs
 |  
 |  show_something(self)
 |  
 |  static_method()
 |      # A static method
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |  
 |  att1 = True
 |  
 |  att2 = 'Foo'

Private Attributes and Methods

Python supports the use of private attributes and methods.

Privacy in this context means that they are hidden from users in various ways:

  • They are not visible in help()
  • They are not imported into the namespace of a calling module — we’ll learn about this later.

However, if a user wishes to call a private method or inspect a private attribute, they can by doing something like this:

my_object._my_private_method()

This can lead to problems, though, since by convention private methods and attributes may change or disappear in future versions of the class.

Classes are meant to be black boxes from the user perspective — you pass and receive data from them in predictable ways without any dependencies on what’s inside the class.

Developers are free to refactor their code over time, significantly changing its internals, so long as the public methods and attributes remain the same.

Static Classes

Static classes are class that don’t have instance methods.

Static classes don’t need to be instantiated.

They can be used to store a collection of stand-alone helper functions as methods.

In the example below, note the absence of self.

class StaticClass():

    def add_these(*nums):
        sum = 0
        for num in nums: sum += num
        return sum
    
    def square_me(x):
        return x**2
StaticClass.add_these(1,5,6)
12
StaticClass.square_me(5)
25

This won’t work:

sc = StaticClass()
sc.square_me(10)
TypeError: StaticClass.square_me() takes 1 positional argument but 2 were given

Classes as Data Structures

Classes are a quick way to store data, similar to dictionaries.

Here we define a static class to store some configuration data.

class MyConfig:
    a = 10
    b = 'foo'
    c = 'something else'
    f = lambda x: x + 10

Objects attributes can be accessed with less typing than dictionary keys.

MyConfig.a
10
MyConfig.f(100)
110

Note also that attributes can be viewed with tab completion (in Jupyter and other IDEs).

You can dynamically add attributes, too.

MyConfig.x = 50

Note that lambda functions are attributes, too, so you can do this:

MyConfig.y = lambda x: x**2
MyConfig.y(100)
10000

It’s harder to add true functions, though.

If you wanted to create a data structure template, you could create a non-static class like this:

class ConfigTemplate:
    
    def __init__(self):
        self.project_name = ''
        self.project_type = ''
        self.default_url = ''
        self.description = ''
config1 = ConfigTemplate()
config1.project_name = 'Apollo'
config1.project_type = 'Moon Mission'
config2 = ConfigTemplate()
config2.project_name = 'Artemis'
config1.project_type = 'Moon Mission'
config1.project_name
'Apollo'
config2.project_name
'Artemis'