Preface jx // Chapter 1: Current Status of Python 1 // Where are we now and where are we going? 2 // What to do with Python 2 3 // Keeping up to date 5 // PEP documents 6 // Active communities 8 // Other resources 11 // Summary 12 // Chapter 2: Modern Python Development Environments 15 // Technical requirements 16 // Python’s packaging ecosystem 17 // Installing Python packages using pip 17 // Isolating the runtime environment 19 // Application-level isolation versus system-level isolation 23 // Application-level environment isolation 24 // Poetry as a dependency management system 27 // System-level environment isolation 32 // Containerization versus virtualization 34 // Virtual environments using Docker 36 // Writing your first Dockerfile 37 // Running containers 41 // Setting up complex environments 43 // Useful Docker and Docker Compose recipes for Python 46 // Virtual development environments using Vagrant 56 // Popular productivity tools 59 // Custom Python shells 59 // Table of Contents // Using IPython 61 // Incorporating shells in your own scripts and programs 65 // Interactive debuggers 66 // Other productivity tools 68 // Summary 70 // Chapter 3: New Things in Python 71 // Technical requirements 72 // Recent language additions 72 // Dictionary merge and update operators 73 // Alternative - Dictionary unpacking 76 // Alternative - ChainMap from the collections module 76 // Assignment expressions 79 // Type-hinting generics 83 // Positional-only parameters 84 // zoneinfo module 87 // graphlib module 88 // Not that new, but still shiny 93 // breakpointO function 93 // Development mode 94 // Module-level getattr () and dir () functions 97 // Formatting strings with f-strings 98 // Underscores in numeric literals 100 // secrets module 100 // What may come in the future? 101 // Union types with the | operator 102 // Structural pattern matching 103 // Summary 108 //
Chapter 4: Python in Comparison with Other Languages 109 // Technical requirements 110 // Class model and object-oriented programming 110 // Accessing super-classes 112 // Multiple inheritance and Method Resolution Order 114 // Class instance initialization 120 // Attribute access patterns 124 // Descriptors 125 // Real-life example - lazily evaluated attributes 128 // Properties 132 // Dynamic polymorphism 138 // Operator overloading 140 // Dunder methods (language protocols) 141 // Comparison to C++ 145 // Table of Contents // Function and method overloading 147 // Single-dispatch functions 149 // Data classes 151 // Functional programming 155 // Lambda functions 157 // The map(), filter(), and reduce() functions 159 // Partial objects and partial functions 162 // Generators 163 // Generator expressions 165 // Decorators 166 // Enumerations 168 // Summary 171 // Chapter 5: Interfaces, Patterns, and Modularity 173 // Technical requirements 174 // Interfaces 175 // A bit of history: zope.interface 177 // Using function annotations and abstract base classes 186 // Using collections.abc 191 // Interfaces through type annotations 192 // Inversion of control and dependency injection 195 // Inversion of control in applications 197 // Using dependency injection frameworks 206 // Summary 212 // Chapter 6: Concurrency 213 // Technical requirements 214 // What is concurrency? 214 // Multithreading 216 // What is multithreading? 217 // How Python deals with threads 221 // When should we use multithreading? 223 // Application responsiveness 223 // Multiuser applications 224 // Work delegation and background processing 225 // An example of a multithreaded application 226 // Using one thread per item 229 // Using a thread pool 231 // Using two-way queues 236 // Dealing with errors in threads 238 // Throttling 241 // Multiprocessing 245 // The built-in multiprocessing module 247 //
Using process pools 251 // Table of Contents // Using multiprocessing.dummy as the multithreading interface 254 // Asynchronous programming 255 // Cooperative multitasking and asynchronous I/O 256 // Python async and await keywords 257 // A practical example of asynchronous programming 262 // Integrating non-asynchronous code with async using futures 265 // Executors and futures 267 // Using executors in an event loop 268 // Summary 269 // Chapter 7: Event-Driven Programming 271 // Technical requirements 272 // What exactly is event-driven programming? 272 // Event-driven != asynchronous 273 // Event-driven programming in GUIs 274 // Event-driven communication 277 // Various styles of event-driven programming 279 // Callback-based style 280 // Subject-based style 281 // Topic-based style 286 // Event-driven architectures 288 // Event and message queues 290 // Summary 293 // Chapter 8: Elements of Metaprogramming 295 // Technical requirements 296 // What is metaprogramming? 296 // Using decorators to modify function behavior before use 297 // One step deeper: class decorators 299 // Intercepting the class instance creation process 304 // Metaclasses 307 // The general syntax 309 // Metaclass usage 312 // Metaclass pitfalls 315 // Using the init subclass () method as an alternative to metaclasses 317 // Codegeneration 319 // exec, eval, and compile 319 // The abstract syntax tree 321 // Import hooks 323 // Notable examples of code generation in Python 323 // Falcon’s compiled router 324 // ? 325 // Summary 326 // Table of Contents // Chapter 9: Bridging Python with ? and C++ 327 // Technical requirements 329 // ? and C++ as the core of Python extensibility 329 // Compiling and loading Python ? extensions 330 // The need to use extensions 332 // Improving performance in critical code sections 333 // Integrating existing code written in different languages 334 //
Integrating third-party dynamic libraries 335 // Creating efficient custom datatypes 335 // Writing extensions 336 // Pure С extensions 337 // A closer look at the Python/C API 341 // Calling and binding conventions 345 // Exception handling 349 // Releasing GIL 351 // Reference counting 353 // Writing extensions with Cython 356 // Cython as a source-to-source compiler 356 // Cython as a language 360 // Downsides of using extensions 362 // Additional complexity 363 // Harder debugging 364 // Interfacing with dynamic libraries without extensions 365 // The ctypes module 365 // Loading libraries 365 // Calling С functions using ctypes 367 // Passing Python functions as ? callbacks 369 // CFFI 372 // Summary 374 // Chapter 10: Testing and Quality Automation 377 // Technical requirements 378 // The principles of test-driven development 379 // Writing tests with pytest 381 // Test parameterization 389 // pytest’s fixtures 392 // Using fakes 402 // Mocks and the unittest.mock module 405 // Quality automation 410 // Test coverage 411 // Style fixers and code linters 415 // Static type analysis 419 // Mutation testing 420 // Table of Contents // Useful testing utilities 427 // Faking realistic data values 427 // Faking time values 429 // Summary 430 // Chapter 11: Packaging and Distributing Python Code 433 // Technical requirements 434 // Packaging and distributing libraries 434 // The anatomy of a Python package 435 // setup.py 438 // setup.cfg 440 // MANIFEST.in 440 // Essential package metadata 442 // Trove classifiers 443 // Types of package distributions 445 // sdist distributions 445 // bdist and wheel distributions 447 // Registering and publishing packages 450 // Package versioning and dependency management 453 // The SemVer standard for semantic versioning 455 // CalVer for calendar versioning 456 // Installing your own packages 457 //
Installing packages directly from sources 457 // Installing packages in editable mode 458 // Namespace packages 459 // Package scripts and entry points 461 // Packaging applications and services for the web 465 // The Twelve-Factor App manifesto 466 // Leveraging Docker 467 // Handling environment variables 470 // The role of environment variables in application frameworks 475 // Creating standalone executables 480 // When standalone executables are useful 481 // Popular tools 481 // Pylnstaller 482 // cx_Freeze 486 // py2exe and py2app 488 // Security of Python code in executable packages 490 // Summary 491 // Chapter 12: Observing Application Behavior and Performance 493 // Technical requirements 494 // Capturing errors and logs 494 // Python logging essentials 495 // Logging system components 497 // Table of Contents // Logging configuration 505 // Good logging practices 509 // Distributed logging 511 // Capturing errors for later review 514 // Instrumenting code with custom metrics 518 // Using Prometheus 520 // Distributed application tracing 530 // Distributed tracing with Jaeger 534 // Summary 540 // Chapter 13: Code Optimization 541 // Technical requirements 542 // Common culprits for bad performance 542 // Code complexity 543 // Cyclomatic complexity 544 // The big 0 notation 545 // Excessive resource allocation and leaks 548 // Excessive I/O and blocking operations 549 // Code profiling 549 // Profiling CPU usage 551 // Macro-profiling 551 // Micro-profiling 557 // Profiling memory usage 560 // Using the objgraph module 562 // ? code memory leaks 570 // Reducing complexity by choosing appropriate data structures 571 // Searching in a list 571 // Using sets 573 // Using the collections module 574 // deque 574 // defaultdict 576 // namedtuple 578 // Leveraging architectural trade-offs 580 // Using heuristics and approximation algorithms 580 //
Using task queues and delayed processing 581 // Using probabilistic data structures 585 // Caching 586 // Deterministic caching 587 // Non-deterministic caching 590 // Summary 595 // Why subscribe? 597 // Other Books You May Enjoy 599 // Index 601