Effective Python by Brett Slatkin is a book filled with best practices of the Python programming language. I devoured the first edition as an ebook and was eager to buy the second edition as a physical book. Having skimmed through it, I was already satisfied. The new edition features updates, the removal of all Python 2 specific hints and workarounds. More specifically, it concentrates on language features of Python 3 up to and including Python 3.8.
However, I was a little confused that there was no tabular information about the additions and the changes. Certainly, it looks like each item was changed in one way or another (as most items in the first edition contained workarounds for Python 2). But if you are like me, owning the first edition of the book and wondering if the new content will be worth it, this blog post got you covered.
In case you want to do the comparison yourself, there is a table of contents of the 2nd edition on the main page of the official web site of the book.
Entirely new items in Effective Python 2nd Edition
There are 27 entirely new items.
- Item 4: Prefer Interpolated F-Strings Over C-style Format Strings and
- Item 6: Prefer Multiple Assignment Unpacking Over Indexing
- Item 10: Prevent Repetition with Assignment Expressions
- Item 13: Prefer Catch-All Unpacking Over Slicing
- Item 14: Sort by Complex Criteria Using the
- Item 15: Be Cautious When Relying on
- Item 16: Prefer
KeyErrorto Handle Missing Dictionary Keys
- Item 17: Prefer
setdefaultto Handle Missing Items in Internal State
- Item 18: Know How to Construct Key-Dependent Default Values with
- Item 19: Never Unpack More Than Three Variables When Functions Return Multiple Values
- Item 29: Avoid Repeated Work in Comprehensions by Using Assignment Expressions
- Item 33: Compose Multiple Generators with
- Item 34: Avoid Injecting Data into Generators with
- Item 35: Avoid Causing State Transitions in Generators with
- Item 36: Consider
itertoolsfor Working with Iterators and Generators
- Item 51: Prefer Class Decorators Over Metaclasses for Composable Class Extensions
- Item 56: Know How to Recognize When Concurrency Is Necessary
- Item 57: Avoid Creating New Thread Instances for On-demand Fan-out
- Item 58: Understand How Using
Queuefor Concurrency Requires Refactoring
- Item 59: Consider
ThreadPoolExecutorWhen Threads Are Necessary for Concurrency
- Item 61: Know How to Port Threaded I/O to
- Item 62: Mix Threads and Coroutines to Ease the Transition to
- Item 63: Avoid Blocking the
asyncioEvent Loop to Maximize Responsiveness
- Item 74: Consider
bytearrayfor Zero-Copy Interactions with bytes
- Item 79: Encapsulate Dependencies to Facilitate Mocking and Testing
- Item 89: Consider
warningsto Refactor and Migrate Usage
- Item 90: Consider Static Analysis via
typingto Obviate Bugs
Items heavily updated in Effective Python 2nd Edition
Most updates to existing items are to only cover code samples for Python 3.7 with some notable exceptions that show 3.8 exclusive samples (the most prominent being the introduction to the walrus operator in items 10 and 29). Most noteworthy, the first item Know Which Version of Python You're Using already makes it clear at the end of the first paragraph:
This book does not cover Python 2.
Keeping this in mind, we expect to see some updates. For instance, Item 3: Know the
str does not mention the Python 2 exclusive
unicode anymore. Even so, there are certain items that have been updated so
much that they contain new advice because of newly added language features.
In particular, I want to mention the following items in this regard.
- Item 48: Validate Subclasses with
__init_subclass__(former Item 33: Validate Subclasses with Metaclasses)
- Item 49: Register Class Existence with
__init_subclass__(former Item 34: Register Class Existence with Metaclasses)
- Item 50: Annotate Class Attributes with
__set_name__(former Item 35: Annotate class attributes with Metaclasses)
- Item 60: Achieve Highly Concurrent I/O with Coroutines (former Item 40: Consider Coroutines to Run Many Functions Concurrently)
While the first three items in this list introduce new language features
restricting the use cases for Metaclasses, the last one was updated to show the
new way of defining Coroutines using the
asyncio built-in module. Since this last item
and the preceding items 56-59 feature Conway's Game of Life as an example, you
might also argue that item 60 belongs in the next section.
Items that have been split up into multiple more elaborate items
There are two items from the first edition that have been split into multiple items in the 2nd edition:
Item 46: Use Built-In Algorithms and Data Structures has been split up into
- Item 71: Prefer
dequefor Producer–Consumer Queues
- Item 72: Consider Searching Sorted Sequences with
- Item 73: Know How to Use
heapqfor Priority Queues
- Item 71: Prefer
Item 56: Test Everything with
Unittesthas been split up into
- Item 76: Verify Related Behaviors in
- Item 77: Isolate Tests from Each Other with
- Item 78: Use Mocks to Test Code with Complex Dependencies
- Item 76: Verify Related Behaviors in
Item 46 from the first edition had more of an overview character, introducing data types from built-in modules. The corresponding items from the 2nd edition are more elaborate and provide an in-depth view with more code samples and example use cases.
Considering the importance of testing generally and particularly in dynamically
typed languages such as Python, I found item 56 from the first edition to be
too brief. In contrast, the new items are much more elaborate on the best practices in
testing. Above all, item 78 about Mocks is a precious addition, giving
an example of how to use the
unittest.mock built-in module to write unit tests
for code depending on a database connection.
To sum it up, Effective Python 2nd Edition adds a lot of new content in comparison to its first edition. More precisely, there are 27 entirely new items. Additionally, two items have been split up into multiple, more elaborate items so that the new edition clocks in at 90 items in comparison to 59 items from the first edition.