Back to Notes

tags:

  • python
  • backend

  • First-class function: A function that is treated like any other value
  • Higher-order function: A function that accepts another function as an argument or returns a function
  • Pure Function: For same arguments it will always returns the same output, - Running them causes no side effects
    • Pure functions are always referentially transparent.
    • "Referential transparency" is a fancy way of saying that a function call can be replaced by its would-be return value because it's the same every time. Referentially transparent functions can be safely memoized. For example add(2, 3) can be smartly replaced by the value 5.

Ternary operator or conditional expression

is_male = True if x == 'male' else False
print(is_male)
#True

List Useful Commands:

https://docs.python.org/3/tutorial/datastructures.html#more-on-lists

Python Built in methods:


Map in Python

  • The map function in Python applies a given function to each item of an iterable (e.g., list) and returns an iterator with the results.
  • This allows for functional operations on elements without explicit loops. It is a key higher-order function used in functional programming to transform data efficiently by separating the transformation logic from the data structure. Using map avoids stateful loop variables, leading to cleaner and more declarative code.
  • Ref: https://docs.python.org/3/library/functions.html#map
def square(num):
    return num * num

numbers = [2, 3, 4]
squared_numbers = map(square, numbers)
print(list(squared_numbers))
# [4, 9, 16]

Filter in Python

def is_even(x):
    return x % 2 == 0

numbers = [1, 2, 3, 4, 5, 6]
evens = list(filter(is_even, numbers))
print(evens)
# [2, 4, 6]

Reduce in Python

  • The functools.reduce() function in Python applies a given function cumulatively to the items of an iterable (like a list), reducing the iterable to a single accumulated value.
  • The function provided should take two arguments: an accumulator and the current value from the iterable. reduce() is a powerful tool for performing operations like summing all numbers in a list or concatenating strings.
  • Ref: https://docs.python.org/3/library/functools.html#functools.reduce
import functools

def add(accumulator, number):
    return accumulator + number

numbers = [10, 20, 30]
total = functools.reduce(add, numbers)
print(total)
# 60

Min in Python

  • This is built in function in the python which applies to iterable or multiple arguments and return the smallest of them.
min(iterable, default, key=None)

min(arg1, arg2, *args, key=None)
  • default specifies an object to return, when iterable is empty and instead of raising the ValueError it will return the default.
  • key specifies a one-argument ordering function like that used for list.sort()

Max in Python

  • It will be same as above it will return the largest element.

The yield keyword

The yield keyword in Python returns a value, kind of like return. However, it's used to turn the function into a generator function.

A generator function creates a new function object. When that function is called, it executes the code in the generator function until it hits a yield statement. At that point, the function pauses and returns the value of the yield statement. The next time the function is called, it picks up right where it left off.

def create_message_generator():
    yield "hi"
    yield "there"
    yield "friend"

gen = create_message_generator()
first = next(gen)
print(first)  # prints: hi
second = next(gen)
print(second)  # prints: there
third = next(gen)
print(third)  # prints: friend

Every time you call create_message_generator(), it creates a new generator instance. To continue from where you left off, you need to assign this generator to a variable (like gen in the example above). This way, when you use next() or loop over the generator, you’re continuing with the same instance rather than starting a new one.


Enums

Ref: https://docs.python.org/3/library/enum.html https://docs.python.org/3/howto/enum.html#enum-basic-tutorial

An Enum is a set of symbolic names bound to unique values. This is useful while defining sum types where we have limited number of values for a given field.

In python we can create Enums using

from enum import Enum
class Weekday(Enum):
    MONDAY = 1
    TUESDAY = 2
    WEDNESDAY = 3
    THURSDAY = 4
    FRIDAY = 5
    SATURDAY = 6
    SUNDAY = 7

# Using the value we can get the member
Weekday(3)
# This will return <Weekday.WEDNESDAY: 3>
print(Weekday.THURSDAY)
# Weekday.THURSDAY
type(Weekday.MONDAY)
# <enum 'Weekday'>
# To just print the name of the Enum member we can use the name attribute
print(Weekday.THURSDAY.name)
# THURSDAY
# likewise they have attribute value also
print(Weekday.WEDNESDAY.value)
# 3
from enum import Enum

enum_type = Enum('COLOR', ["RED", "BLUE"])

print(enum_type.COLOR.RED)

Ord function

  • Given a string representing one Unicode character, return an integer representing the Unicode code point of that character. For example, ord('a') returns the integer 97 and ord('€') (Euro sign) returns 8364. This is the inverse of chr().

Generators

Generators are a simple and powerful tool for creating iterators. They are written like regular functions but use the yield statement whenever they want to return data. Each time next() is called on it, the generator resumes where it left off (it remembers all the data values and which statement was last executed). An example shows that generators can be trivially easy to create:

def reverse(data):
    for index in range(len(data)-1, -1, -1):
        yield data[index]

for char in reverse('golf'):
	print(char)

Anything that can be done with generators can also be done with class-based iterators as described in the previous section. What makes generators so compact is that the __iter__() and __next__() methods are created automatically.

Another key feature is that the local variables and execution state are automatically saved between calls. This made the function easier to write and much more clear than an approach using instance variables like self.index and self.data.

In addition to automatic method creation and saving program state, when generators terminate, they automatically raise StopIteration. In combination, these features make it easy to create iterators with no more effort than writing a regular function.

Generator Expressions

Some simple generators can be coded succinctly as expressions using a syntax similar to list comprehensions but with parentheses instead of square brackets. These expressions are designed for situations where the generator is used right away by an enclosing function. Generator expressions are more compact but less versatile than full generator definitions and tend to be more memory friendly than equivalent list comprehensions.

Examples:

sum(i*i for i in range(10))                 # sum of squares
#285

xvec = [10, 20, 30]
yvec = [7, 5, 3]
sum(x*y for x,y in zip(xvec, yvec))         # dot product
#260

unique_words = set(word for line in page  for word in line.split())

valedictorian = max((student.gpa, student.name) for student in graduates)

data = 'golf'
list(data[i] for i in range(len(data)-1, -1, -1))
# ['f', 'l', 'o', 'g']