Python Workspace¶

Intro¶

In [1]:
a = 4
print(a)
4

Python Coding Tips: Lessons I Wish I Knew When I Started Coding¶

by Livia Ellen published on Medium

In [2]:
# f-string dynamic string formattin
name = "John"
age = 26
message = f"My name is {name}, and I am {age} years old."
print(message)
My name is John, and I am 26 years old.
In [4]:
# decorator
import time
def timer_decorator(func):
  def wrapper(*args, **kwargs):
    start_time = time.time()
    result = func(*args, **kwargs)
    end_time = time.time()
    print(f'{func.__name__} executed in {end_time - start_time} seconds')
    return result
  return wrapper

def fibonacci_of(n):
  if n in {0, 1}:  # Base case
    return n
  return fibonacci_of(n - 1) + fibonacci_of(n - 2)  # Recursive case

@timer_decorator
def example_function():
  print(fibonacci_of(10))

example_function()
55
example_function executed in 0.0014181137084960938
In [2]:
# decorator
import time
def timer_decorator(func):
  def wrapper(*args, **kwargs):
    start_time = time.time()
    result = func(*args, **kwargs)
    end_time = time.time()
    print(f'{func.__name__} executed in {end_time - start_time} seconds')
    return result
  return wrapper

def fibonacci_of(n):
  if n in {0, 1}:  # Base case
    return n
  return fibonacci_of(n - 1) + fibonacci_of(n - 2)  # Recursive case

@timer_decorator
def example_function():
  print(fibonacci_of(10))

example_function()

# decorator as a context
from contextlib import contextmanager
@contextmanager
def timer_b(name):
  try:
    # print(f'TIMER: {name} start')
    start_time = time.time()
    yield
  finally:
    end_time = time.time()
    print(f'TIMER: {name} executed in {end_time - start_time} seconds')

@timer_b(name='fibonacci')
def example_function_b():
  print(fibonacci_of(10))
example_function_b()

with timer_b(name='fibonacci timer'):
  print(fibonacci_of(10))
55
example_function executed in 0.0004971027374267578 seconds
55
TIMER: fibonacci executed in 4.029273986816406e-05 seconds
55
TIMER: fibonacci timer executed in 3.647804260253906e-05 seconds
In [6]:
# help() Function
def calculate_square(number):
  """
  Calculates the square of a given number.Parameters:
  - number (int): The input number.
  Returns:
  - int: The square of the input number.
  """
  return number ** 2

# accessing help for the calculate_square function
help(calculate_square)

print(calculate_square(10))
Help on function calculate_square in module __main__:

calculate_square(number)
    Calculates the square of a given number.Parameters:
    - number (int): The input number.
    Returns:
    - int: The square of the input number.

100
In [7]:
# list comprehensions
# finding squares of even numbers in a range
squares = [x**2 for x in range(10) if x % 2 == 0]
print(squares)
[0, 4, 16, 36, 64]
In [8]:
# lambda functions
# adding two numbers with a lambda function
add_numbers = lambda x, y: x + y
result = add_numbers(3, 5)
print(result)
8
In [10]:
# iteration with enumerate and zip
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 22]
# enumerate for index and value
for index, name in enumerate(names):
  print(f"Person {index + 1}: {name}")
print('...')
# zip for parallel iteration
for name, age in zip(names, ages):
  print(f"{name} is {age} years old.")
Person 1: Alice
Person 2: Bob
Person 3: Charlie
...
Alice is 25 years old.
Bob is 30 years old.
Charlie is 22 years old.
In [26]:
# *args and **kwargs
def multiply_args(*args, **kwargs):
  result = 1
  for num in args:
    result *= num
  return result
print(multiply_args(2,3,4))

def multiply_kwargs(*args, **kwargs):
  result = 1
  for key, value in kwargs.items():
    result *= value
  return result
print(multiply_kwargs(two=2,three=3,four=4))
24
24
In [27]:
# error Handling with try and except
# Example: Graceful error handling with try and except
def divide_numbers(a, b):
  try:
    result = a / b
    print(f"The result of {a} divided by {b} is: {result}")
  except ZeroDivisionError:
    print("Cannot divide by zero! Please provide a non-zero denominator.")
  except Exception as e:
    print(f"An unexpected error occurred: {e}")
  else:
    print("Division successful!")
# Testing the function
divide_numbers(10, 2)  # Normal division
divide_numbers(5, 0)   # Division by zero
divide_numbers("a", 2) # Unexpected error (TypeError)
The result of 10 divided by 2 is: 5.0
Division successful!
Cannot divide by zero! Please provide a non-zero denominator.
An unexpected error occurred: unsupported operand type(s) for /: 'str' and 'int'
In [28]:
# list slicing:
# extracting a sublist from index 2 to 5
original_list = [1, 2, 3, 4, 5, 6, 7]
sublist = original_list[2:6]
print(sublist)
[3, 4, 5, 6]
In [31]:
# generators
# fibonacci sequence generator
def fibonacci(n):
  a, b = 0, 1
  for _ in range(n):
    yield a
    a, b = b, a + b
for n in fibonacci(10):
  print(n)
0
1
1
2
3
5
8
13
21
34
In [32]:
# assertions
# assertion for checking if a variable is positive
num = -5
assert num > 0, "Number must be positive"
---------------------------------------------------------------------------
AssertionError                            Traceback (most recent call last)
Cell In[32], line 4
      1 # assertions
      2 # assertion for checking if a variable is positive
      3 num = -5
----> 4 assert num > 0, "Number must be positive"

AssertionError: Number must be positive
In [36]:
# namedtuples: self-documenting data structures
from collections import namedtuple
Person = namedtuple('Person', ['name', 'age'])
alice = Person(name="Alice", age=30)
print(alice)
print(alice.name)
print(alice.age)
Person(name='Alice', age=30)
Alice
30
In [37]:
# zipping lists: combine sequences
# matching user inputs with corresponding answers in a quiz
names = ["Alice", "Bob"]
scores = [85, 92]
for name, score in zip(names, scores):
    print(f"{name}: {score}")
Alice: 85
Bob: 92
In [39]:
# dictionaries
data = {"name": "Alice"}
age = data.get("age", 30)
data.setdefault("country", "USA")
print(data, age)
{'name': 'Alice', 'country': 'USA'} 30
In [40]:
# the asterisk (*) operator
# passing a dynamic list of values to a function expecting separate arguments
def func(a, b, c):
  return a + b + c

values = [1, 2, 3]
print(func(*values))
6
In [41]:
# merging dictionaries
# using the update() method
dict1 = {'a': 1, 'b': 2}
dict2 = {'b': 3, 'c': 4}
dict1.update(dict2)
print(dict1)
{'a': 1, 'b': 3, 'c': 4}

Context Managers In Python¶

by Yanick Andrade published on Medium

In [5]:
class AuthManager:
  def __init__(self, username: str, password: str) -> None:
    print('init')
    self.username = username
    self.password = password

  def __enter__(self):
    print('enter')
    return self

  def __exit__(self, exc_type, exc_value, traceback):
    print('exit')
    # let's assume that we have some logic to log user out
    self.logout()
    # we will se why we are returning True
    return True

  def login(self):
    print('login')
    # let's assume that we have some logic to log user in
    pass

  def logout(self):
    print('logout')
    # let's assume that we have some logic to log user out
    pass

with AuthManager(username="random", password="random") as auth:
  # let's assume that we have some logic to log user in
  auth.login()
init
enter
login
exit
logout
In [3]:
# decorator as a context
from contextlib import contextmanager
import time
@contextmanager
def timer_b(name):
  try:
    # print(f'TIMER: {name} start')
    start_time = time.time()
    yield
  finally:
    end_time = time.time()
    print(f'TIMER: {name} executed in {end_time - start_time} seconds')

@timer_b(name='fibonacci')
def example_function_b():
  print(fibonacci_of(10))
example_function_b()

with timer_b(name='fibonacci timer'):
  print(fibonacci_of(10))
55
TIMER: fibonacci executed in 0.0008521080017089844 seconds
55
TIMER: fibonacci timer executed in 4.3392181396484375e-05 seconds

How to Filter a List in Python¶

by Soumya Agarwal

comparing methods for filtering lists in Python

Method Example Advantages Disadvantages
filter() result = filter(lambda x: x%2 == 0, num_list) (i) Efficient for complex conditions or reusable filters; (ii) Concise and readable (i) Not as efficient as list comprehension for simple cases; (ii) Requires defining a separate function.
List comprehension result = [x for x in num_list if x%2 == 0] (i) Concise and efficient for simple filtering. (i) Less flexible than filter() for complex conditions; (ii) Can be less readable with complex logic.
Loop result = []for x in num_list: if x%2 == 0: new_list.append(x) (i) Easiest to understand for beginners (i) Less efficient than filter() or list comprehension; (ii) Longer and less readable for complex tasks.
Built-in functions result = max(num_list) (i) Highly efficient for specific tasks like finding min/max; (ii) Built-in and easy to use. (i) Limited to specific tasks; (ii) Not suitable for more complex filtering criteria.

using the filter() function¶

In [8]:
nums = [11, 22, 31, 42, 51]

def is_num_odd(z):
  return z % 2 != 0

out_nums = filter(is_num_odd, nums)
# Convert the iterator to a list
out_nums = list(out_nums)
print(out_nums)
[11, 31, 51]

using list comprehension¶

In [7]:
nums = [11, 22, 31, 42, 51]
result = [x for x in nums if x % 2 == 0]
print(result)
[22, 42]

using a loop¶

In [9]:
nums = [11, 22, 31, 42, 51]
outNums = []
for n in nums:
  if n % 2 == 0:
    outNums.append(n)
print(outNums)
[22, 42]

filtering using regular expressions¶

In [10]:
import re

texts = [
  "My email is johndoe@example.com.",
  "Contact me at johnsmith123@gmail.com",
  "This website has no contact information."
]
emails = [re.findall(r"[\w.]+@\w+\.\w+", text) for text in texts]
print(emails)
[['johndoe@example.com'], ['johnsmith123@gmail.com'], []]