Source: Classes and Objects
Objects are an encapsulation of variables and functions into a single entity. Objects get their variables and functions from classes. Classes are essentially a template to create your objects.
You can create multiple different objects that are of the same class(have the same variables and functions defined). However, each object contains independent copies of the variables defined in the class.
class MyClass:
variable = "blah"
def function(self):
print("This is a message inside the class.")
myobjectx = MyClass()
myobjecty = MyClass()
myobjecty.variable = "yackity"
# Then print out both values
print(myobjectx.variable)
print(myobjecty.variable)
Notice that
myobjectx
andmyobjecty
both call the class ofMyClass()
To access a function inside of an object you use notation similar to accessing a variable.
You can now call specific parts of that class as a variable using dot notation (Note: the previous class code block)
Thinking Recursively
Source: Thinking Recursively
“Of all ideas I have introduced to children, recursion stands out as the one idea that is particularly able to evoke an excited response.”
— Seymour Papert, Mindstorms
Iterative code will loop through each instance of a given input (i.e. array) and perform the function on each individual value of the input.
A recursive function is a function defined in terms of itself via self-referential expressions
This means that the function will continue to call itself and repeat its behavior until some condition is met to return a result. All recursive functions share a common structure made up of two parts: base case and recursive case.
def factorial_recursive(n):
# Base case: 1! = 1
if n == 1:
return 1
# Recursive case: n! = n * (n-1)!
else:
return n * factorial_recursive(n-1)
>>> factorial_recursive(5)
120
When dealing with recursive functions, keep in mind that each recursive call has its own execution context, so to maintain state during recursion you have to either:
Thread the state through each recursive call so that the current state is part of the current call’s execution context
Keep the state in global scope
def sum_recursive(current_number, accumulated_sum):
# Base case
# Return the final state
if current_number == 11:
return accumulated_sum
# Recursive case
# Thread the state through the recursive call
else:
return sum_recursive(current_number + 1, accumulated_sum + current_number)
# Pass the initial state
>>> sum_recursive(1, 0)
55
# Global mutable state
current_number = 1
accumulated_sum = 0
def sum_recursive():
global current_number
global accumulated_sum
# Base case
if current_number == 11:
return accumulated_sum
# Recursive case
else:
accumulated_sum = accumulated_sum + current_number
current_number = current_number + 1
return sum_recursive()
>>> sum_recursive()
55
A data structure is recursive if it can be defined in terms of a smaller version of itself. A list is an example of a recursive data structure.
# Return a new list that is the result of
# adding element to the head (i.e. front) of input_list
def attach_head(element, input_list):
return [element] + input_list
attach_head(1, # Will return [1, 46, -31, "hello"]
attach_head(46, # Will return [46, -31, "hello"]
attach_head(-31, # Will return [-31, "hello"]
attach_head("hello", [])))) # Will return ["hello"]
[1, 46, -31, 'hello']
Starting with an empty list, you can generate any list by recursively applying the attach_head function, and thus the list data structure can be defined recursively as:
+---- attach_head(element, smaller list)
list = +
+---- empty list
Recursion can also be seen as self-referential function composition. We apply a function to an argument, then pass that result on as an argument to a second application of the same function, and so on. Repeatedly composing attach_head with itself is the same as attach_head calling itself repeatedly.
A Naive recursion algorithm is usually the most obvious solution when one is asked a problem. It may not be a smart algorithm but will probably get the job done (…eventually.)
Source: What is a “naive” algorithm?
Naively following the recursive definition of the nth Fibonacci number was rather inefficient. This can cause us to unnecessarily recompute values.
Source: Pytest Fixtures and Coverage