Download presentation
Presentation is loading. Please wait.
1
Idiomatic Python James Edwards
2
What are idioms? Natural language: “expressions or phrases that characterise a language” Programming: clean and simple way of expressing one particular (generally simple) task
3
Why should I use them? Readability Maintainability Peer-review
Idioms are often easy to read Maintainability As it’s easier to understand it’s easier to modify in the future Peer-review Source code is becoming more frequently requested by journals
4
Caveat You don’t always have to follow these!
Learn by doing. Over time: Do some programming Read good source code and spot the patterns Review your own code Spot the flaws and identify where it can be improved Repeat
5
Simpler examples
6
Swapping Values Traditionally, use a temporary variable:
temp = a a = b b = temp The Python idiom: b, a = a, b
7
Checking for Substrings
Make sure to use the “in” keyword: if “world” in “Hello world”: print(“Found”) But not for single characters: if “-+” in “-+*/”: print(“This prints”) # Use a tuple/set instead if “-+” in (“-”, “+”, “*”, “/”): print(“Not found”)
8
Using dictionaries You can access dictionary values like this:
veg_dict = {“pepper”: 1, “carrot”: 2} key = “potato” value = veg_dict[key] # raises a KeyError You may get an exception. To protect against this, you may want to use the get method: value = veg_dict.get(key) # sets value to None And if you want to supply your own default value: value = veg_dict.get(key, default=“no vegetable”)
9
Looping – 1 Using indexing in loops? This is the anti-idiom:
sentence = (“Hello”, “world!”) for index in range(len(sentence)): print(sentence[index]) The Pythonic Way: for word in sentence: print(word)
10
Looping – 2 What about if you need the index? You could do this:
for word in sentence: print(index, word) index += 1 But there’s a better way! Use the enumerate function: for index, word in enumerate(sentence):
11
Looping – 3 Reverse looping as encountered in many languages:
for index in range(len(sentence)-1, -1, -1): print(sentence[index]) Remember, Python has “batteries included”: for word in reversed(sentence): print(word) There are many similarly useful built in functions, such as zip, map, sorted, filter and others
12
Looping – 4 No need to manually extract from a list of tuples/lists:
people = [(“Joe”, “Bloggs”), (“Josie”, “Bloggs”)] for person in people: first_name, surname = person[0], person[1] print(first_name, surname) You can extract tuple/list values natively: for first_name, surname in people:
13
Looping – 5 (yes, more) How to loop over dictionary keys only:
words = {1: “hello”, 2: “world”} for key in words: print(key) Or if you want to delete or add key/value pairs to a dictionary in a loop: for key in list(words.keys()): # makes a copy if key == 2: del words[key]
14
Looping – 6 Need dictionary values only? No need to do this:
for key in words: print(words[key]) Instead, use this method: for word in words.values(): print(word) And finally, for both the key and value: for key, word in words.items(): print(key, word)
15
Sticking strings together
This is generally bad for lengthy lists (performance), but is OK if it’s only a few: sentence = “” for word in words: sentence += “ ” + word Instead, use join with a list: sentence = “ ”.join(words) NB: CPython 3+ is pretty fast with appending byte strings, Unicode strings should still use join.
16
Really long strings You could do something like this:
long_text = “This is a really, really, really,” +\ “ really, really, really, long string” But, Python lets you just use brackets: long_text = (“This is a really, really, really, ” “really, really, really long string”)
17
Checking for identity Use the “is” keyword for identity:
a = “A” letter = a letter is a # evaluates to True The difference between equality and identity: other = “a”.upper() other == a # evaluates to True other is a # evaluates to False
18
Checking for identity The “is not” construction is its own operator:
2 is not 3 # evaluates to True You can see it behaves differently to “is” and “not” separately: 2 is (not 3) # False, as “not 3” evaluates False
19
Checking for None When checking for None, don’t test for equality:
if thing == None: print(“Works, but should be used sparingly”) Instead, use an identity check (“is”): if thing is None: print(“Preferred method to check for None”)
20
File handling Clunky, and also risks leaving file handle open if close() is forgotten: file_handle = open(“file.txt”, “w”) file_handle.write(“Hello world”) file_handle.close() Using the “with” keyword: with open(“file.txt”, “w”) as file_handle:
21
Use the standard library
Don’t reinvent the wheel. Standard library includes lots of modules you may need: Various file format parsers Regular expressions & other text tools Advanced logging system Basic testing packages Debugger and profiler Data compression High-level threading and multiprocessing And lots of others!
22
Use the PyPI PyPI is the Python Package Index – a vast number of packages to do many things you might want to Others have done the hard work so you don’t have to: fuzzywuzzy: fuzzy string matching scikit-learn: machine learning libraries Flask, Django etc.: web frameworks MPInfo: lookup information on UK Members of Parliament
23
More sophisticated vernacular
24
List comprehensions Constructing a simple list? Try this instead:
squares = list() for i in range(10): squares.append(i**2) Try this instead: squares = list(i**2 for i in range(10)) You can also impose conditions: sq3 = list(i**2 for i in range(10) if i % 2 == 0)
25
Reversing dict mappings
Use a dictionary comprehension: boring_dict = {“first”: “A”, “second”: “B”} exciting_dict = {val: key for key, val in boring_dict.items()} Note, this particular construct: only works for dictionaries with immutable values, such as strings, integers, tuples etc. doesn’t work with list values
26
A rule of thumb for comprehensions
If the comprehension is longer than a line, consider using a normal for loop! Comprehensions are often less readable for more complex expressions: synth_signal = ((int(x[0]) | (int(x[1]) << 32)) for x in np.array([alarm_word, mask_word]).T)
27
Checking for overlaps Naïve implementation of intersection:
residents = [“Alice”, “Bob”, “Oskar”] staff = [“Alex”, “Oskar”, “Divya”] Naïve implementation of intersection: intersection = list(person for person in residents if person in staff) Better is to use sets (usually also faster): intersection = set(residents) & set(staff)
28
Multiple comparisons Unnecessary: Chained (transient) comparisons:
if 1 < years and years < 5: print(“More than 1 but less than 5 years”) Chained (transient) comparisons: if 1 < years < 5:
29
Handling failures Look Before You Leap (LBYL):
if os.path.exists(“critical.dat”): # Another process may have deleted the file with open(“critical.dat”) as file_handle: print(file_handle.readlines()) else: print(“critical.dat has gone!”)
30
Look Before You Leap Race conditions can appear (as just seen) Intent may be obscured: lots of checks to verify conditions before doing what you want Anticipating all error conditions can get ugly
31
Handling failures Easier to Ask Forgiveness than Permission (EAFP):
try: with open(“critical.dat”) as file_handle: print(file_handle.readlines()) except FileNotFoundError: print(“critical.dat has gone!”) Ask yourself: is the code more readable with EAFP? If so, then it’s Pythonic But it has drawbacks…
32
Easier to Ask for Forgiveness
Possible side effects if try block has too much code Take care to only wrap the code that can raise exceptions with the try statement Can’t be used within comprehensions, generators or lambda functions
33
Try/Except/Else/Finally
Use else when the try block succeeds: external_db = database.open_connection() try: external_db.add_person(“James”, “Edwards”) except ValidationError: print(“Database cannot validate new record”) else: # no exception raised print(“Added record to database”) finally: # always carried out external_db.close_connection()
34
Looping – 7 Need to detect a particular break condition in a for loop? For example, you could use a “sentinel”: found = False # this is a sentinel for i in range(7): if i == 8: found = True print(“Found 8”) break if not found: print(“8 not found”)
35
Looping – 7 However, for loops can use an “else” clause instead:
for i in range(7): if i == 8: print(“Found 8”) break else: print(“8 not found”) Else clause reached when the loop isn’t broken with a break or return
36
Subtleties of sentinels
This won’t work as you might expect: def super_append(element, the_list=[]): the_list.append(element) return the_list >>> super_append(1) ? >>> super_append(2)
37
Subtleties of sentinels
This won’t work as you might expect: def super_append(element, the_list=[]): the_list.append(element) return the_list >>> super_append(1) [1] >>> super_append(2) [1, 2]
38
Subtleties of sentinels
To get the correct behaviour, use a sentinel value and modify the function to detect it: def super_append(element, the_list=None): if the_list is None: # None most commonly used the_list = list() the_list.append(element) return the_list >>> super_append(1) [1] >>> super_append(2) [2]
39
Thanks for listening
40
Extra resources Some resources/people with good advice:
PEP8 – Official style guide for Python Appropriate use of idioms Hitchhikers Guide to Python: Code Style Raymond Hettinger – excellent talks on code style Jeff Knupp (but keep the zealotry in mind) Python idioms (YouTube) Google
Similar presentations
© 2025 SlidePlayer.com Inc.
All rights reserved.