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.
- 4. Prefer Interpolated F-Strings Over C-style Format Strings and
- 6. Prefer Multiple Assignment Unpacking Over Indexing
- 10. Prevent Repetition with Assignment Expressions
- 13. Prefer Catch-All Unpacking Over Slicing
- 14. Sort by Complex Criteria Using the
- 15. Be Cautious When Relying on
- 16. Prefer
KeyErrorto Handle Missing Dictionary Keys
- 17. Prefer
setdefaultto Handle Missing Items in Internal State
- 18. Know How to Construct Key-Dependent Default Values with
- 19. Never Unpack More Than Three Variables When Functions Return Multiple Values
- 29. Avoid Repeated Work in Comprehensions by Using Assignment Expressions
- 33. Compose Multiple Generators with
- 34. Avoid Injecting Data into Generators with
- 35. Avoid Causing State Transitions in Generators with
- 36. Consider
itertoolsfor Working with Iterators and Generators
- 51. Prefer Class Decorators Over Metaclasses for Composable Class Extensions
- 56. Know How to Recognize When Concurrency Is Necessary
- 57. Avoid Creating New Thread Instances for On-demand Fan-out
- 58. Understand How Using
Queuefor Concurrency Requires Refactoring
- 59. Consider
ThreadPoolExecutorWhen Threads Are Necessary for Concurrency
- 61. Know How to Port Threaded I/O to
- 62. Mix Threads and Coroutines to Ease the Transition to
- 63. Avoid Blocking the
asyncioEvent Loop to Maximize Responsiveness
- 74. Consider
bytearrayfor Zero-Copy Interactions with bytes
- 79. Encapsulate Dependencies to Facilitate Mocking and Testing
- 89. Consider
warningsto Refactor and Migrate Usage
- 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 Differences Between
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.
- 48. Validate Subclasses with
__init_subclass__(former Item 33: Validate Subclasses with Metaclasses)
- 49. Register Class Existence with
__init_subclass__(former Item 34: Register Class Existence with Metaclasses)
- 50. Annotate Class Attributes with
__set_name__(former Item 35: Annotate class attributes with Metaclasses)
- 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
- 71. Prefer
dequefor Producer–Consumer Queues
- 72. Consider Searching Sorted Sequences with
- 73. Know How to Use
heapqfor Priority Queues
- 71. Prefer
- Item 56: Test Everything with
Unittesthas been split up into
- 76. Verify Related Behaviors in
- 77. Isolate Tests from Each Other with
- 78. Use Mocks to Test Code with Complex Dependencies
- 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.