NB: More About Classes

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 usuall CamelCase, but not instances
  • When saving to a file:
    • If only one class, use a lower case name of the class
    • If more than one, use a package name, one that stands for the logical group the classes belong to

Anatomy of a Python Class

## Import class dependencies here
import pandas as pd
import numpy as np
import re
import requests
from lxml import etree

## 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
    def static_method():
        pass

Privacy

Users are free to access private attributes and methods, but they not shown in the help docs.

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'

Static Classes

Static classes don’t need to be instantiated.

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

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

Unlike dictionaries, their keys, or attributes, can be accessed with less typing.

MyConfig.a
10

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

MyConfig.f(100)
110

You can dynamically add attributes, too.

MyConfig.x = 50

Note that lambda functions are attributes, too:

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 = ''
        
    def myf(self):
        pass
config1 = ConfigTemplate()
config1.project_name = 'Apollo'
config1.project_type = 'Moon Mission'
config2 = ConfigTemplate()
config2.project_name = 'Artemis'
config1.project_type = 'Moon Mission'

Another use case is using a class to store a collection of data frames as a model.


class Model: pass

Model.WINE = ...
Model.PLACE = ...
Model.REVIEW = ...

This will make it easier to access these dataframes later.

For example, when saving them, you won’t have to track down the variables you used in your code.

You can use the built-in .__dict__ method of any object see all of its attributes.

config1.__dict__.keys()
dict_keys(['project_name', 'project_type', 'default_url', 'description'])

So, to save our wine review model above, we could do something like this:

for att in Model.__dict__:
    df = getattr(Model, att)
    df.to_sql(att, db)