Fluent Python: Clear, Concise, and Effective Programming
Rate it:
Open Preview
Read between January 10, 2023 - January 1, 2024
11%
Flag icon
In addition to enforcing uniqueness, the set types implement many set operations as infix operators, so, given two sets a and b, a | b returns their union, a & b computes the intersection, a - b the difference, and a ^ b the symmetric difference.
11%
Flag icon
Adding elements to a set may change the order of existing elements. That’s because the algorithm becomes less efficient if the hash table is more than two-thirds full, so Python may need to move and resize the table as it grows. When this happens, elements are reinserted and their relative ordering may change.
Andrew Breza
Why?
13%
Flag icon
Code that has to run on multiple machines or on multiple occasions should never depend on encoding defaults. Always pass an explicit encoding= argument when opening text files, because the default may change from one machine to the next, or from one day to the next.
14%
Flag icon
from unicodedata import normalize def nfc_equal(str1, str2):     return normalize('NFC', str1) == normalize('NFC', str2) def fold_equal(str1, str2):     return (normalize('NFC', str1).casefold() ==             normalize('NFC', str2).casefold())
17%
Flag icon
A typed named tuple can also be constructed with the fields given as keyword arguments, like this: Coordinate = typing.NamedTuple('Coordinate', lat=float, lon=float) This is more readable, and also lets you provide the mapping of fields and types as **fields_and_types.
17%
Flag icon
The collections.namedtuple function is a factory that builds subclasses of tuple enhanced with field names, a class name, and an informative __repr__. Classes built with namedtuple can be used anywhere where tuples are needed, and in fact many functions of the Python standard library that are used to return tuples now return named tuples for convenience,
18%
Flag icon
Now let’s see how a DemoDataClass instance looks: >>> dc = DemoDataClass(9) >>> dc.a 9 >>> dc.b 1.1 >>> dc.c 'spam' Again, a and b are instance attributes, and c is a class attribute we get via the instance.
Andrew Breza
?
22%
Flag icon
Weak references is a very specialized topic. That’s why I chose to skip it in this second edition. Instead, I published “Weak References” on fluentpython.com
22%
Flag icon
Using mutable objects as default values for function parameters is dangerous because if the parameters are changed in place, then the default is changed, affecting every future call that relies on the default.
22%
Flag icon
In some situations, it may be useful to hold a reference to an object that will not—by itself—keep an object alive. One example is a class that wants to keep track of all its current instances. This can be done with weak references, a low-level mechanism underlying the more useful collections WeakValueDictionary, WeakKeyDictionary, WeakSet, and the finalize function from the weakref module. For more on this, please see “Weak References” at fluentpython.com.
24%
Flag icon
If you work with text from many languages, you may want to apply unicode.normalize('NFC', s) to any string s before comparing or storing it. If you do that often, it’s handy to have an nfc function to do so, as in Example 7-17. Example 7-17. Building a convenient Unicode normalizing function with partial >>> import unicodedata, functools >>> nfc = functools.partial(unicodedata.normalize, 'NFC') >>> s1 = 'café' >>> s2 = 'cafe\u0301' >>> s1, s2 ('café', 'café') >>> s1 == s2 False >>> nfc(s1) == nfc(s2) True
25%
Flag icon
See PEP 3102—Keyword-Only Arguments if you are interested in the rationale and use cases for that feature.
26%
Flag icon
We can write str | bytes instead of Union[str, bytes] since Python 3.10. It’s less typing, and there’s no need to import Optional or Union from typing. Contrast the old and new syntax for the type hint of the plural parameter of show_count: plural: Optional[str] = None    # before plural: str | None = None       # after
27%
Flag icon
To annotate a tuple with many fields, or specific types of tuple your code uses in many places, I highly recommend using typing.NamedTuple,
27%
Flag icon
in general it’s better to use abc.Mapping or abc.MutableMapping in parameter type hints, instead of dict
27%
Flag icon
Explicit TypeAlias in Python 3.10 PEP 613—Explicit Type Aliases introduced a special type, TypeAlias, to make the assignments that create type aliases more visible and easier to type check. Starting with Python 3.10, this is the preferred way to create type aliases: from typing import TypeAlias FromTo: TypeAlias = tuple[str, str]
29%
Flag icon
Type hints are a complex and evolving topic. Fortunately, they are an optional feature. Let us keep Python accessible to the widest user base and stop preaching that all Python code should have type hints—as
31%
Flag icon
a closure is a function that retains the bindings of the free variables that exist when the function is defined, so that they can be used later when the function is invoked and the defining scope is no longer available.
34%
Flag icon
Example 10-8. The promos list is built by introspection of a new promotions module from decimal import Decimal import inspect from strategy import Order import promotions promos = [func for _, func in inspect.getmembers(promotions, inspect.isfunction)]
35%
Flag icon
sometimes you may encounter a design pattern or an API that requires that components implement an interface with a single method, and that method has a generic-sounding name such as “execute,” “run,” or “do_it.” Such patterns or APIs often can be implemented with less boilerplate code in Python using functions as first-class objects.
35%
Flag icon
Every object-oriented language has at least one standard way of getting a string representation from any object. Python has two: repr() Return a string representing the object as the developer wants to see it. It’s what you get when the Python console or a debugger shows an object. str() Return a string representing the object as the user wants to see it. It’s what you get when you print() an object.
37%
Flag icon
if you define a class attribute named __slots__ holding a sequence of attribute names, Python uses an alternative storage model for the instance attributes: the attributes named in __slots__ are stored in a hidden array or references that use less memory than a dict.
38%
Flag icon
It’s possible to “save memory and eat it too”: if you add the '__dict__' name to the __slots__ list, your instances will keep attributes named in __slots__ in the per-instance array of references, but will also support dynamically created attributes, which will be stored in the usual __dict__.
47%
Flag icon
we have four complementary ways of programming with interfaces in modern Python, each with different advantages and drawbacks. You are likely to find suitable use cases for each typing scheme in any modern Python codebase of significant size. Rejecting any one of these approaches will make your work as a Python programmer harder than it needs to be.
49%
Flag icon
In my experience, GUI toolkits are where inheritance is most useful.
49%
Flag icon
Despite the problems, Tkinter is stable, flexible, and provides a modern look-and-feel if you use the tkinter.ttk package and its themed widgets. Also, some of the original widgets, like Canvas and Text, are incredibly powerful. You can turn a Canvas object into a simple drag-and-drop drawing application in a matter of hours. Tkinter and Tcl/Tk are definitely worth a look if you are interested in GUI programming.
49%
Flag icon
So perhaps the best advice about inheritance is: avoid it if you can. But often, we don’t have a choice: the frameworks we use impose their own design choices.
49%
Flag icon
I read the first edition of Grady Booch et al., Object-Oriented Analysis and Design, 3rd ed., and highly recommend it as a general primer on object-oriented thinking, independent of programming language. It is a rare book that covers multiple inheritance without prejudice.
51%
Flag icon
Of course, not all typing mishaps can be fixed with cast. Sometimes we need # type: ignore, the occasional Any, or even leaving a function without type hints.
57%
Flag icon
If you are about to implement comparison operators, study functools.total_ordering. That is a class decorator that automatically generates methods for all rich comparison operators in any class that defines at least a couple of them.
57%
Flag icon
When I see patterns in my programs, I consider it a sign of trouble. The shape of a program should reflect only the problem it needs to solve. Any other regularity in the code is a sign, to me at least, that I’m using abstractions that aren’t powerful enough—often that I’m generating by hand the expansions of some macro that I need to write. Paul Graham,
57%
Flag icon
If a class provides __getitem__, the iter() built-in accepts an instance of that class as iterable and builds an iterator from the instance. Python’s iteration machinery will call __getitem__ with indexes starting from 0, and will take an IndexError as a signal that there are no more items.
58%
Flag icon
Don’t Make the Iterable an Iterator for Itself A common cause of errors in building iterables and iterators is to confuse the two. To be clear: iterables have an __iter__ method that instantiates a new iterator every time. Iterators implement a __next__ method that returns individual items, and an __iter__ method that returns self. Therefore, iterators are also iterable, but iterables are not iterators.
59%
Flag icon
class Sentence:     def __init__(self, text):         self.text = text     def __repr__(self):         return f'Sentence({reprlib.repr(self.text)})'     def __iter__(self):         return (match.group() for match in RE_WORD.finditer(self.text)) The only difference from Example 17-8 is the __iter__ method, which here is not a generator function (it has no yield) but uses a generator expression to build a generator and then returns it. The end result is the same: the caller of __iter__ gets a generator object.
59%
Flag icon
My rule of thumb in choosing the syntax to use is simple: if the generator expression spans more than a couple of lines, I prefer to code a generator function for the sake of readability.
60%
Flag icon
when implementing generators, know what is available in the standard library, otherwise there’s a good chance you’ll reinvent the wheel.
64%
Flag icon
The Python community is finding new, creative uses for context managers. Some examples from the standard library are: Managing transactions in the sqlite3 module—see “Using the connection as a context manager”. Safely handling locks, conditions, and semaphores—as described in the threading module documentation. Setting up custom environments for arithmetic operations with Decimal objects—see the decimal.localcontext documentation. Patching objects for testing—see the unittest.mock.patch function.
65%
Flag icon
Using @contextmanager The @contextmanager decorator is an elegant and practical tool that brings together three distinctive Python features: a function decorator, a generator, and the with statement. Using @contextmanager reduces the boilerplate of creating a context manager: instead of writing a whole class with __enter__/__exit__ methods, you just implement a generator with a single yield that should produce whatever you want the __enter__ method to return. In a generator decorated with @contextmanager, yield splits the body of the function in two parts: everything before the yield will be ...more
65%
Flag icon
A little-known feature of @contextmanager is that the generators decorated with it can also be used as decorators themselves.
66%
Flag icon
Extensive use of recursion and minimal use of assignment are hallmarks of programming in a functional style.
67%
Flag icon
The semantics of for/else, while/else, and try/else are closely related, but very different from if/else. Initially, the word else actually hindered my understanding of these features, but eventually I got used to it. Here are the rules: for The else block will run only if and when the for loop runs to completion (i.e., not if the for is aborted with a break). while The else block will run only if and when the while loop exits because the condition became falsy (i.e., not if the while is aborted with a break). try The else block will run only if no exception is raised in the try block.
68%
Flag icon
with is not just for resource management; it’s a tool for factoring out common setup and teardown code, or any pair of operations that need to be done before and after another procedure.
69%
Flag icon
Never use time.sleep(…) in asyncio coroutines unless you want to pause your whole program. If a coroutine needs to spend some time doing nothing, it should await asyncio.sleep(DELAY).
74%
Flag icon
from time import sleep, strftime from concurrent import futures def display(*args):       print(strftime('[%H:%M:%S]'), end=' ')     print(*args) def loiter(n):       msg = '{}loiter({}): doing nothing for {}s...'     display(msg.format('\t'*n, n, n))     sleep(n)     msg = '{}loiter({}): done.'     display(msg.format('\t'*n, n))     return n * 10   def main():     display('Script starting.')     executor = futures.ThreadPoolExecutor(max_workers=3)       results = executor.map(loiter, range(5))       display('results:', results)       display('Waiting for individual results:')    ...more
75%
Flag icon
Python threads are well suited for I/O-intensive applications, and the concurrent.futures package makes it relatively simple to use for certain use cases.
95%
Flag icon
Besides PEP 8, other influential style guides are the Google Python Style Guide and the Pocoo Styleguide, from the team that brought us Flake, Sphinx, Jinja 2, and other great Python libraries.