COMP1531

3.2 - Python - Pythonic

 

Being Pythonic

Being "Pythonic" means that your code generally follows a set of idioms agreed upon by the broader python community.

 

"When a veteran Python developer calls portions of code not “Pythonic”, they usually mean that these lines of code do not follow the common guidelines and fail to express its intent in what is considered the most readable way. On some border cases, no best way has been agreed upon on how to express an intent in Python code, but these cases are rare."

 

Hitchhiker's guide to python (read more on this)

Examples

  • Docstrings
  • Map, reduce, filter, lambdas
  • Exceptions > Early returns
  • Destructuring, ignored variables
  • Enumerate
  • Multi line strings

Docstrings

Docstrings are an important way to document code and make clear to other programmers the intent and meaning behind what you're writing. We are somewhat different on the formatting, but we want it to include 1) Description, 2) Parameters, 3) Returns

def string_find(str1, str2):
    """ Returns whether str2 can be found within str1

    Parameters:
        str1 (str): The haystack
        str2 (str): The needle

    Returns:
        (bool): Whether or not str2 could be found in str1

    """

docstring.py

Map, Reduce, Filter

  • Map: creates a new list with the results of calling a provided function on every element in the given list

 

  • Reduce: executes a reducer function (that you provide) on each member of the array resulting in a single output value

 

  • Filter: creates a new array with all elements that pass the test implemented by the provided function

Map

Map: creates a new array with the results of calling a provided function on every element in the calling array

def shout(string):
    return string.upper() + "!!!!"

if __name__ == '__main__':
    tutors = ['Simon', 'Teresa', 'Kaiqi', 'Michelle']
    angry_tutors = list(map(shout, tutors))
    print(angry_tutors)

map.py

Reduce

Reduce: executes a reducer function (that you provide) on each member of the array resulting in a single output value

from functools import reduce

def custom_sum(first, second):
    return first + second

if __name__ == '__main__':
    studentMarks = [ 55, 43, 34, 23, 22, 10, 44 ]
    total = reduce(lambda a, b: a + b, studentMarks)
    print(total)

reduce.py

Filter

Filter: creates a new array with all elements that pass the test implemented by the provided function

from functools import reduce

if __name__ == '__main__':
    marks = [ 65, 72, 81, 40, 56 ]
    passing_marks = list(filter(lambda m: m >= 50, marks))
    total = reduce(lambda a, b: a + b, passing_marks)
    average = total/len(passing_marks)
    print(average)

filter.py

Combined

from functools import reduce

if __name__ == '__main__':
    marks = [ 39, 43.2, 48.6, 24, 33.6 ] # Marks out of 60
    normalised_marks = map(lambda m: 100*m/60, marks)
    passing_marks = list(filter(lambda m: m >= 50, normalised_marks))
    total = reduce(lambda a, b: a + b, passing_marks)
    average = total/len(passing_marks)
    print(average)

allthree.py

Exceptions > Early Returns

You might be quite familiar with early returns:

def sqrt(num):
    if num < 0:
        return None
    return num ** 0.5
    
myNum = int(input())
if sqrt(myNum) is not None:
    print(sqrt(myNum))

early.py

The problems though are:

  • Often we can only use "None" or some arbitrary return (-1) to signify that it didn't work
  • It's harder to check for a client using it

Exceptions > Early Returns

So we use exceptions. And we can make our own.

class SqrtException(Exception):
    pass

def sqrt(num):
    if num < 0:
        raise SqrtException("Number cannot be < 0")
    return num ** 0.5

try:
    print(sqrt(int(input())))
except SqrtException as e:
    print(e)

early.py

Destructuring

Being able to make tuples and destructure them is very powerful. If you don't want all tuples you can use blanks to ignore them.

import math

def convert(x, y):
    return (math.sqrt(x**2 + y**2), math.degrees(math.atan2(y,x)))

if __name__ == '__main__':
    print("Enter x coord: ", end='')
    x = int(input())
    print("Enter y coord: ", end='')
    y = int(input())

    mag, dir = convert(x, y)
    print(mag, dir)

    mag2, _ = convert(x, y)
    print(mag2)

destructure.py

Enumerate

Sometimes we want to iterate cleanly over the values in a list, but also know what index we're up to. In these situations the enumerate built-in is useful.

tutors = ['Vivian', 'Rob', 'Rudra', 'Michelle']

for idx, name in enumerate(tutors):
    print(f"{idx + 1}: {name}")

enumerate.py

Multi-line strings

Someones strings need to exist over multiple lines, there are two good approaches for this

if __name__ == '__main__':
    text1 = """hi

    this has lots of space

    between chunks"""

    text2 = (
        "This is how you can break strings "
        "into multiple lines "
        "without needing to combine them manually"
    )

    print(text1)
    print(text2)

multiline.py

Made with Slides.com