📄 Page
1
High Performance Python Practical Performant Programming for Humans Micha Gorelick & Ian Ozsvald Foreword by Hilary Mason 3rd Edition
📄 Page
2
9 7 8 1 0 9 8 1 6 5 9 6 3 5 6 5 9 9 ISBN: 978-1-098-16596-3 US $65.99 CAN $82.99 PY THON / PROGR AMMING Your Python code may run correctly, but what if you need it to run faster? This practical book shows you how to locate performance bottlenecks and significantly speed up your code in high-data-volume programs. By explaining the fundamental theory behind design choices, this expanded edition of High Performance Python helps experienced Python programmers gain a deeper understanding of Python’s implementation. How do you take advantage of multicore architectures or compilation? Or build a system that scales up beyond RAM limits or with a GPU? Authors Micha Gorelick and Ian Ozsvald reveal concrete solutions to many issues and include war stories from companies that use high-performance Python for GenAI data extraction, productionized machine learning, and more. • Get a better grasp of NumPy, Cython, and profilers • Learn how Python abstracts the underlying computer architecture • Use profiling to find bottlenecks in CPU time and memory usage • Write efficient programs by choosing appropriate data structures • Speed up matrix and vector computations • Process DataFrames quickly with Pandas, Dask, and Polars • Speed up your neural networks and GPU computations • Use tools to compile Python down to machine code • Manage multiple I/O and computational operations concurrently • Convert multiprocessing code to run on local or remote clusters High Performance Python “Ian and Micha’s new book combines an overview of modern performance tooling with deep insights into general principles of code optimization, making it an essential read for every Python developer.” Mikhail Timonin, quant developer, Engelhart Micha Gorelick is a machine learning researcher and engineer, an educator, and an advocate for socially conscious computing. Ian Ozsvald is a London-based independent chief data scientist who coaches teams, teaches, and creates data products.
📄 Page
3
Micha Gorelick and Ian Ozsvald Foreword by Hilary Mason High Performance Python Practical Performant Programming for Humans THIRD EDITION
📄 Page
4
978-1-098-16596-3 [LSI] High Performance Python by Micha Gorelick and Ian Ozsvald Copyright © 2025 Micha Gorelick and Ian Ozsvald. All rights reserved. Printed in the United States of America. Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA 95472. O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (http://oreilly.com). For more information, contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com. Acquisitions Editor: Michelle Smith Development Editor: Sara Hunter Production Editor: Clare Laylock Copyeditor: Dwight Ramsey Proofreader: Arthur Johnson Indexer: Potomac Indexing, LLC Interior Designer: David Futato Cover Designer: Jose Marzan Illustrator: Kate Dullea September 2014: First Edition May 2020: Second Edition May 2025: Third Edition Revision History for the Third Edition 2025-04-29: First Release See http://oreilly.com/catalog/errata.csp?isbn=9781098165963 for release details. The O’Reilly logo is a registered trademark of O’Reilly Media, Inc. High Performance Python, the cover image, and related trade dress are trademarks of O’Reilly Media, Inc. The views expressed in this work are those of the authors and do not represent the publisher’s views. While the publisher and the authors have used good faith efforts to ensure that the information and instructions contained in this work are accurate, the publisher and the authors disclaim all responsibility for errors or omissions, including without limitation responsibility for damages resulting from the use of or reliance on this work. Use of the information and instructions contained in this work is at your own risk. If any code samples or other technology this work contains or describes is subject to open source licenses or the intellectual property rights of others, it is your responsibility to ensure that your use thereof complies with such licenses and/or rights. High Performance Python is available under the Creative Commons Attribution-NonCommercial- NoDerivs 3.0 License.
📄 Page
5
Table of Contents Foreword. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ix Preface. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi 1. Understanding Performant Python. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 The Fundamental Computer System 2 Computing Units 2 Memory Units 6 Communications Layers 8 Idealized Computing Versus the Python Virtual Machine 10 Idealized Computing 11 Python’s Virtual Machine 12 So Why Use Python? 15 How to Be a Highly Performant Programmer 17 Good Working Practices 18 Optimizing for the Team Rather than the Code Block 21 The Remote Performant Programmer 23 Some Thoughts on Good Notebook Practice 23 Your Work 24 The Future of Python 25 Where Did the GIL Go? 25 Does Python Have a JIT? 25 Wrap-Up 26 2. Profiling to Find Bottlenecks. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 Profiling Efficiently 28 Introducing the Julia Set 30 Calculating the Full Julia Set 33 iii
📄 Page
6
Simple Approaches to Timing—print and a Decorator 36 Simple Timing Using the Unix time Command 39 Using the cProfile Module 41 Visualizing cProfile Output with SnakeViz 46 Using line_profiler for Line-by-Line Measurements 48 Using memory_profiler to Diagnose Memory Usage 53 Combining CPU and Memory Profiling with Scalene 61 Introspecting an Existing Process with PySpy 63 VizTracer for an Interactive Time-Based Call Stack 65 Bytecode: Under the Hood 67 Using the dis Module to Examine CPython Bytecode 67 Digging into Bytecode Specialization with Specialist 69 Different Approaches, Different Complexity 71 Unit Testing During Optimization to Maintain Correctness 73 No-op @profile Decorator 74 Strategies to Profile Your Code Successfully 77 Wrap-Up 78 3. Lists and Tuples. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 A More Efficient Search 82 Lists Versus Tuples 85 Lists as Dynamic Arrays 86 Tuples as Static Arrays 90 Wrap-Up 93 4. Dictionaries and Sets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 How Do Dictionaries and Sets Work? 99 Inserting and Retrieving 99 Deletion 104 Resizing 104 Hash Functions and Entropy 106 Wrap-Up 111 5. Iterators and Generators. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 Iterators for Infinite Series 118 Lazy Generator Evaluation 120 Wrap-Up 124 6. Matrix and Vector Computation. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125 Introduction to the Problem 126 Aren’t Python Lists Good Enough? 131 Problems with Allocating Too Much 133 iv | Table of Contents
📄 Page
7
Memory Fragmentation 136 Understanding perf 139 Making Decisions with perf ’s Output 142 Enter numpy 143 Applying numpy to the Diffusion Problem 146 Memory Allocations and In-Place Operations 149 Selective Optimizations: Finding What Needs to Be Fixed 153 numexpr: Making In-Place Operations Faster and Easier 156 Graphics Processing Units (GPUs) 158 Dynamic Graphs: PyTorch 159 GPU Speed and Numerical Precision 162 GPU-Specific Operations 165 Basic GPU Profiling 168 Performance Considerations of GPUs 170 When to Use GPUs 172 Deep Learning Performance Considerations 174 A Cautionary Tale: Verify “Optimizations” (scipy) 179 Lessons from Matrix Optimizations 180 Wrap-Up 183 7. Pandas, Dask, and Polars. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185 Pandas 186 Pandas’s Internal Model 187 Arrow and NumPy 189 Applying a Function to Many Rows of Data 189 Numba to Compile NumPy for Pandas 199 Building from Partial Results Rather than Concatenating 200 There’s More Than One (and Possibly a Faster) Way to Do a Job 202 Advice for Effective Pandas Development 203 Dask for Distributed Data Structures and DataFrames 205 Diagnostics 206 Parallel Pandas with Dask 207 Parallelized apply with Swifter on Dask 209 Polars for Fast DataFrames 210 Wrap-Up 211 8. Compiling to C. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213 What Sort of Speed Gains Are Possible? 214 JIT Versus AOT Compilers 216 Why Does Type Information Help the Code Run Faster? 216 Using a C Compiler 217 Reviewing the Julia Set Example 218 Table of Contents | v
📄 Page
8
Cython 219 Compiling a Pure Python Version Using Cython 219 pyximport 221 Cython Annotations to Analyze a Block of Code 222 Adding Some Type Annotations 224 Cython and numpy 229 Parallelizing the Solution with OpenMP on One Machine 232 Numba 234 PyPy 236 Garbage Collection Differences 238 Running PyPy and Installing Modules 238 A Summary of Speed Improvements 240 When to Use Each Technology 241 Foreign Function Interfaces 242 ctypes 243 cffi 246 f2py 249 CPython Extensions: C 252 CPython Extensions: Rust 256 Wrap-Up 260 9. Asynchronous I/O. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 261 Introduction to Asynchronous Programming 263 How Does async/await Work? 267 Serial Web Crawler 268 Asynchronous Web Crawler 270 Shared CPU–I/O Workload 275 Serial CPU Workload 276 Batched CPU Workload 278 Fully Asynchronous CPU Workload 281 Wrap-Up 286 10. The multiprocessing Module. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289 An Overview of the multiprocessing Module 292 Estimating Pi Using the Monte Carlo Method 294 Estimating Pi Using Processes and Threads 296 Using Python Objects 296 Replacing multiprocessing with Joblib 304 Random Numbers in Parallel Systems 308 Using numpy 309 Finding Prime Numbers 312 Queues of Work 319 vi | Table of Contents
📄 Page
9
Asynchronously Adding Jobs to the Queue 323 Verifying Primes Using Interprocess Communication 324 Serial Solution 329 Naive Pool Solution 329 A Less Naive Pool Solution 330 Using manager.Value as a Flag 331 Using Redis as a Flag 333 Using RawValue as a Flag 336 Using mmap as a Flag 337 Using mmap as a Flag Redux 338 Sharing numpy Data with multiprocessing 340 Synchronizing File and Variable Access 348 File Locking 348 Locking a Value 352 Wrap-Up 356 11. Clusters and Job Queues. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357 Benefits of Clustering 358 Drawbacks of Clustering 359 $462 Million Wall Street Loss Through Poor Cluster Upgrade Strategy 360 Skype’s 24-Hour Global Outage 361 Common Cluster Designs 362 How to Start a Clustered Solution 362 Ways to Avoid Pain When Using Clusters 363 Two Clustering Solutions 365 Using IPython Parallel to Support Research 365 Message Brokering for Cluster Efficiency 368 Other Clustering Tools to Look At 372 Docker 373 Docker’s Performance 373 Advantages of Docker 377 Wrap-Up 378 12. Using Less RAM. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379 Objects for Primitives Are Expensive 380 The array Module Stores Many Primitive Objects Cheaply 382 Using Less RAM in NumPy with NumExpr 385 Understanding the RAM Used in a Collection 389 Bytes Versus Unicode 390 Efficiently Storing Lots of Text in RAM 391 Trying These Approaches on 11 Million Tokens 392 Modeling More Text with scikit-learn’s FeatureHasher 400 Table of Contents | vii
📄 Page
10
Introducing DictVectorizer and FeatureHasher 401 Comparing DictVectorizer and FeatureHasher on a Real Problem 404 SciPy’s Sparse Matrices 405 Tips for Using Less RAM 408 Probabilistic Data Structures 409 Very Approximate Counting with a 1-Byte Morris Counter 410 K-Minimum Values 413 Bloom Filters 417 LogLog Counter 423 Real-World Example 427 Wrap-Up 430 13. Lessons from the Field. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 431 Developing a High Performance Machine Learning Algorithm 432 High Performance Computing in Journalism 435 Lessons from the Field of Cyber Reinsurance 441 Python in Quant Finance 451 Maintain Flexibility to Achieve High Performance 455 Streamlining Feature Engineering Pipelines with Feature-engine (2020) 458 Highly Performant Data Science Teams (2020) 464 Numba (2020) 468 Optimizing Versus Thinking (2020) 474 Making Deep Learning Fly with RadimRehurek.com (2014) 477 Large-Scale Social Media Analysis at Smesh (2014) 483 Index. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 487 viii | Table of Contents
📄 Page
11
Foreword When you think about high performance computing, you might imagine giant clus‐ ters of machines modeling complex weather phenomena or trying to understand signals in data collected about far-off stars. It’s easy to assume that only people building specialized systems should worry about the performance characteristics of their code. By picking up this book, you’ve taken a step toward learning the theory and practices you’ll need to write highly performant code. Every programmer can benefit from understanding how to build performant systems. There is an obvious set of applications that are just on the edge of possible, and you won’t be able to approach them without writing optimally performant code. If that’s your practice, you’re in the right place. But there is a much broader set of applications that can benefit from performant code. We often think that new technical capabilities are what drives innovation, but I’m equally fond of capabilities that increase the accessibility of technology by orders of magnitude. When something becomes ten times cheaper in time or compute costs, suddenly the set of applications you can address is wider than you imagined. The first time this principle manifested in my own work was over a decade ago, when I was working at a social media company, and we ran an analysis over multiple terabytes of data to determine whether people clicked on more photos of cats or dogs on social media. It was dogs, of course. Cats just have better branding. This was an outstandingly frivolous use of compute time and infrastructure at the time! Gaining the ability to apply techniques that had previously been restricted to sufficiently high-value applications, such as fraud detection, to a seemingly trivial question opened up a new world of now-accessible possibilities. We were able to take what we learned from these experiments and build a whole new set of products in search and content discovery. ix
📄 Page
12
For an example that you might encounter today, consider a machine learning system that recognizes unexpected animals or people in security video footage. A sufficiently performant system could allow you to embed that model into the camera itself, improving privacy or, even if running in the cloud, using significantly less compute and power—benefiting the environment and reducing your operating costs. This can free up resources for you to look at adjacent problems, potentially building a more valuable system. We all desire to create systems that are effective, easy to understand, and performant. Unfortunately, it often feels like we have to pick only two (or one) out of the three! High Performance Python is a handbook for people who want to make things that are capable of all three. This book stands apart from other texts on the subject in three ways. First, it’s written for us—humans who write code. You’ll find all of the context you need to understand why you might make certain choices. Second, Gorelick and Ozsvald do a wonderful job of curating and explaining the necessary theory to support that context. You’ll also learn the specific quirks of the most useful libraries for implementing these approaches today. This is one of a rare class of programming books that will change the way you think about the practice of programming. I’ve given this book to many people who could benefit from the additional tools it provides. The ideas that you’ll explore in its pages will make you a better programmer, no matter what language or environment you choose to work in. Enjoy the adventure. — Hilary Mason, Cofounder & CEO at Hidden Door x | Foreword
📄 Page
13
Preface Python is easy to learn. You’re probably here because now that your code runs correctly, you need it to run faster. You like the fact that your code is easy to modify and you can iterate with ideas quickly. The trade-off between easy to develop and runs as quickly as I need is a well-understood and often-bemoaned phenomenon. There are solutions. Some people have serial processes that have to run faster. Others have problems that could take advantage of multicore architectures, clusters, or graphics processing units. Some need scalable systems that can process more or less as expediency and funds allow, without losing reliability. Others will realize that their coding techniques, often borrowed from other languages, perhaps aren’t as natural as examples they see from others. In this book we will cover all of these topics, giving practical guidance for under‐ standing bottlenecks and producing faster and more scalable solutions. We also include some war stories from those who went ahead of you, who took the knocks so you don’t have to. Python is well suited for rapid development, production deployments, and scalable systems. The ecosystem is full of people who are working to make it scale on your behalf, leaving you more time to focus on the more challenging tasks around you. Who This Book Is For You’ve used Python for long enough to have an idea about why certain things are slow and to have seen technologies like Cython, numpy, and PyPy being discussed as possible solutions. You might also have programmed with other languages and so know that there’s more than one way to solve a performance problem. While this book is primarily aimed at people with CPU-bound problems, we also look at data transfer and memory-bound solutions. Typically, these problems are faced by scientists, engineers, quants, and academics. xi
📄 Page
14
We also look at problems that a web developer might face, including the movement of data and the use of just-in-time (JIT) compilers like PyPy and asynchronous I/O for easy-win performance gains. It might help if you have a background in C (or C++, or maybe Java), but it isn’t a prerequisite. Python’s most common interpreter (CPython—the standard you nor‐ mally get if you type python at the command line) is written in C, and so the hooks and libraries all expose the gory inner C machinery. There are lots of other techniques that we cover that don’t assume any knowledge of C. You might also have a lower-level knowledge of the CPU, memory architecture, and data buses, but again, that’s not strictly necessary. Who This Book Is Not For This book is meant for intermediate to advanced Python programmers. Motivated novice Python programmers may be able to follow along as well, but we recommend having a solid Python foundation. We don’t cover storage-system optimization. If you have a SQL or NoSQL bottleneck, then this book probably won’t help you. What You’ll Learn Your authors have been working with large volumes of data, a requirement for I want the answers faster! and a need for scalable architectures, for many years in both industry and academia. We’ll try to impart our hard-won experience to save you from making the mistakes that we’ve made. At the start of each chapter, we’ll list questions that the following text should answer. (If it doesn’t, tell us and we’ll fix it in the next revision!) We cover the following topics: • Background on the machinery of a computer so you know what’s happening behind the scenes • Lists and tuples—the subtle semantic and speed differences in these fundamental data structures • Dictionaries and sets—memory allocation strategies and access algorithms in these important data structures • Iterators—how to write in a more Pythonic way and open the door to infinite data streams using iteration • Pure Python approaches—how to use Python and its modules effectively xii | Preface
📄 Page
15
• Matrices with numpy—how to use the beloved numpy library like a beast • Compilation and just-in-time computing—processing faster by compiling down to machine code, making sure you’re guided by the results of profiling • Concurrency—ways to move data efficiently • multiprocessing—various ways to use the built-in multiprocessing library for parallel computing and to efficiently share numpy matrices, and some costs and benefits of interprocess communication (IPC) • Cluster computing—convert your multiprocessing code to run on a local or remote cluster for both research and production systems • Using less RAM—approaches to solving large problems without buying a humongous computer • Lessons from the field—lessons encoded in war stories from those who took the blows so you don’t have to Python 3 Python 3 is the standard version of Python as of 2020, with Python 2.7 deprecated after a 10-year migration process. If you’re still on Python 2.7, you’re doing it wrong—many libraries are no longer supported for your line of Python, and support will become more expensive over time. Please do the community a favor and migrate to Python 3, and make sure that all new projects use Python 3. Unless stated other‐ wise we’re using Python 3.12 (released 2023). In this book, we use 64-bit Python. While 32-bit Python is supported, it is far less common for scientific work. We’d expect all the libraries to work as usual, but numeric precision, which depends on the number of bits available for counting, is likely to change. 64-bit is dominant in this field, along with *nix environments (often Linux or Mac). 64-bit lets you address larger amounts of RAM. *nix lets you build applications that can be deployed and configured in well-understood ways with well-understood behaviors. If you’re a Windows user, you’ll have to buckle up. Most of what we show will work just fine, but some things are OS-specific, and you’ll have to research a Windows solution. The biggest difficulty a Windows user might face is the installation of modules: research in sites like Stack Overflow should give you the solutions you need. If you’re on Windows, having a virtual machine (e.g., using VirtualBox) with a running Linux installation might help you to experiment more freely. Windows users should definitely look at a packaged solution like those available through Anaconda, Canopy, Python(x,y), or Sage. These same distributions will make the lives of Linux and Mac users far simpler too. Preface | xiii
📄 Page
16
License This book is licensed under Creative Commons Attribution-NonCommercial- NoDerivs 3.0. You’re welcome to use this book for noncommercial purposes, including for noncom‐ mercial teaching. The license allows only for complete reproductions; for partial reproductions, please contact O’Reilly (see “How to Contact Us” on page xvi). Please attribute the book as noted in the following section. We negotiated that the book should have a Creative Commons license so the contents could spread further around the world. We’d be quite happy to receive a beer if this decision has helped you. We suspect that the O’Reilly staff would feel similarly about the beer. How to Make an Attribution The Creative Commons license requires that you attribute your use of a part of this book. Attribution just means that you should write something that someone else can follow to find this book. The following would be sensible: “High Performance Python, 3rd ed., by Micha Gorelick and Ian Ozsvald (O’Reilly). Copyright 2025 Micha Gorelick and Ian Ozsvald, 978-1-098-16596-3.” Using Code Examples Supplemental material (code examples, exercises, etc.) is available for download at https://oreil.ly/supp-hpp3e. If you have a technical question or a problem using the code examples, please email support@oreilly.com. This book is here to help you get your job done. In general, if example code is offered with this book, you may use it in your programs and documentation. You do not need to contact us for permission unless you’re reproducing a significant portion of the code. For example, writing a program that uses several chunks of code from this book does not require permission. Selling or distributing examples from O’Reilly books does require permission. Answering a question by citing this book and quoting example code does not require permission. Incorporating a significant amount of example code from this book into your product’s documentation does require permission. We appreciate, but generally do not require, attribution (see the previous section for attribution). If you feel your use of code examples falls outside fair use or the permission given above, feel free to contact us at permissions@oreilly.com. xiv | Preface
📄 Page
17
Errata and Feedback We encourage you to review this book on public sites like Amazon—please help others understand if they would benefit from this book! You can also email us at feedback@highperformancepython.com. We’re particularly keen to hear about errors in the book, successful use cases where the book has helped you, and high performance techniques that we should cover in the next edition. You can access the web page for this book at https://oreil.ly/supp- hpp3e. Complaints are welcomed through the instant-complaint-transmission-service > /dev/null. Conventions Used in This Book The following typographical conventions are used in this book: Italic Indicates new terms, URLs, email addresses, filenames, and file extensions. Constant width Used for program listings, as well as within paragraphs to refer to program elements such as variable or function names, databases, data types, environment variables, statements, and keywords. Constant width bold Shows commands or other text that should be typed literally by the user. Constant width italic Shows text that should be replaced with user-supplied values or by values deter‐ mined by context. This element signifies a tip or suggestion. This element signifies a general note. Preface | xv
📄 Page
18
This element indicates a warning or caution. O’Reilly Online Learning For more than 40 years, O’Reilly Media has provided technol‐ ogy and business training, knowledge, and insight to help companies succeed. Our unique network of experts and innovators share their knowledge and expertise through books, articles, and our online learning platform. O’Reilly’s online learning platform gives you on-demand access to live training courses, in-depth learning paths, interactive coding environments, and a vast collection of text and video from O’Reilly and 200+ other publishers. For more information, visit https://oreilly.com. How to Contact Us Please address comments and questions concerning this book to the publisher: O’Reilly Media, Inc. 1005 Gravenstein Highway North Sebastopol, CA 95472 800-889-8969 (in the United States or Canada) 707-827-7019 (international or local) 707-829-0104 (fax) support@oreilly.com https://oreilly.com/about/contact.html We have a web page for this book, where we list errata, examples, and any additional information. You can access this page at https://oreil.ly/hpp-3e. For news and information about our books and courses, visit https://oreilly.com. Find us on LinkedIn: https://linkedin.com/company/oreilly-media. Watch us on YouTube: https://youtube.com/oreillymedia. xvi | Preface
📄 Page
19
Acknowledgments Hilary Mason wrote our foreword—thanks for composing such a wonderful opening narrative for our book. Giles Weaver, Alexander Mitchell, Mani Sarkar, and Pete Bleackley provided invaluable technical feedback on this edition. Thanks to Patrick Cooper, Kyran Dale, Dan Foreman-Mackey, Calvin Giles, Brian Granger, Jamie Matthews, John Montgomery, Christian Schou Oxvig, Matt “snakes” Reiferson, Balthazar Rouberol, Michael Skirpan, Luke Underwood, Jake Vanderplas, and William Winter for invaluable feedback and contributions. Ian thanks his wife, Emily, for letting him disappear for another eight months to write this third edition (thankfully, she’s terribly understanding). Ian apologizes to his dog for sitting and writing rather than walking in the woods quite as much as she’d have liked. Micha thanks Storm, Velvet, Matthias, Oscar, and the rest of her friends and family for being so patient while she learned to write. O’Reilly editors are rather lovely to work with; do strongly consider talking to them if you want to write your own book. Our contributors to the “Lessons from the Field” chapter very kindly shared their time and hard-won lessons. We give thanks to Martin Goodson, James Poynter, David Rawlinson, Mikhail Timonin, and Leon Yin for this edition, and to Soledad Galli, Andrew Godwin, Valentin Haenel, Ben Jackson, Alex Kelly, Radim Řehůřek, Marko Tasic, Sebastjan Trepca, Linda Uruchurtu, and Vincent D. Warmerdam for their time and effort during the previous editions. Preface | xvii
📄 Page
20
(This page has no text content)