Fluent Python Notes
A work in progress collection of my notes on what I learned while reading Fluent Python by Luciano Ramalho.
Chapter 3: Dictionaries and Sets
Mappings (think dicts) can be merged into a new mapping using the
|
operator>>> dict_1 = {'a':1, 'b':2} >>> dict_2 = {'c':3, 'd':4} >>> dict_1 | dict_2 {'a': 1, 'b': 2, 'c': 3, 'd': 4}
Mappings can be merged in place with the
|=
operatordict_1 = {'a':1, 'b':2} dict_2 = {'c':3, 'd':4} >>> dict_1 |= dict_2 >>> dict_1 {'a': 1, 'b': 2, 'c': 3, 'd': 4}
dict.setdefault
will set the default value for the key if not in the dictionary and will return the value so it can be updated at the same time.>>> my_dict = {} >>> my_dict.setdefault("things", []).append("thing 1") >>> my_dict {'things': ['thing 1']}
A
defaultdict
will only create the missing value when accessed with bracket notation (my_dict["missing_key"]
) and not when accessed with the.get()
method.MappingProxyType
from thetypes
module creates a read-only instance of the original mapping, but it is dynamically updated when the original mapping is updated.The
.keys()
,.values()
, and.items()
dictionary methods all return dictionary view instances of classesdict_keys
,dict_values
, anddict_items
. These views are read only and are dynamic proxies of the dictionary. If the dictionary is updated, instances of these views will be updated immediately.Looking up values in a
dict
is very fast, but comes at the expense of more memory use than other data structures due to it being a hash tables."Set elements must be hashable. They
set
type is not hashable, so you can't build aset
with nestedset
elements. Butfrozenset
is hashable, so you can havefrozenset
elements inside aset
."To create an empty
set
, useset()
.{}
will create an emptydict
, not an emptyset
.Adding elements to a
set
may change the order of the elements.
Chapter 5: Data Class Builders
- To set a mutable default value for a dataclass field, use
default_factory
which will create a new default for each instance of the dataclass instead of all instances sharing the same mutable object.dataclass
will flag it if you don't do this forlist
,dict
, orset
, but won't for other mutable values.
@dataclass
class Class:
students: list = field(default_factory=list)
A
__post_init__
method can be added to a dataclass that will be called after__init__
. This can be used to validate fields or compute field values based on other fields.Referring to a data class as a code smell - "The main idea of object-oriented programming is to place behavior and data together in the same code unit: a class. If a class is widely used but has no significant behavior of its own, it's possible that code dealing with its instances is scattered (and even duplicated) in methods and functions throughout the system -- a recipe for maintenance headaches. That's why Fowler's refactorings to deal with a data class involve bringing responsibilities back into it.
Chapter 6: Object References, Mutability, and Recycling
Variables are assigned to objects, not objects to variables. Instead of saying an object was assigned to a variable, it's more accurate to say a variable is bound to an object.
Objects must exist before a variable name can be bound to it.
The only common use case for the
is
operator is checking if something isNone
. Most of the time opt for==
instead ofis
since==
also works with checking forNone
.Tuples are containers so they hold references to objects. Tuples are only immutable in that you can't change which objects are referenced to, but if those references are to mutable objects, then you can change those objects. For example, if a tuple contains a list, you can't assign a different list to the tuple, but you can modify the list that is referenced in the tuple.
Python parameter passing is "call by sharing". Each parameter of a function get a copy of each reference in the arguments. The parameters are aliases of the actual arguments. A function can change a mutable object passed as a parameter but it can't replace immutable objects. This means, for example, that if a list is passed to a function, any changes to the list in the function will affect the list outside of the function. But any changes to an immutable object won't affect the object outside of the function.
Default mutable values for function parameters is a bad idea. Each default value is evaluated when the function is defined. This means that every call of the function will use the same object the parameter references. If the parameter is modified in one function call, the next function call will be using the same, and now modified, parameter.
In this example,
self.players
is an alias toplayers
.players
is an alias for the default list when no argument is given.
class Team:
def __init__(self, players=[]):
self. players = players
Instead of setting a parameter to a default mutable value, set the default value to
None
then in__init__
check if the parameter is None. If yes, set the attribute to the desired default.When a mutable argument is passed to a function, unless you want any changes made to the object to appear outside of the function, you should make a copy of the object when assigning it to the instance variable.
A complete example of proper handling of mutable arguments
class Team:
def __init__(self, players=None):
if players is None:
self.players = []
else:
self. players = list(player) # Makes a copy of players
del
deletes references to objects and not the actual object. An object will be garbage collected when there are no more references to it.For a tuple
t
,tuple(t)
andt[:]
return a reference tot
and not a copy.
Chapter 7: Functions as First-Class Objects
map
andfilter
have mostly been replaced by using list comprehensions or generator expressions.reduce
is mostly replaced by the built insum
function since summing a collection of items is the most common use case.There are types of callable objects: user-defined functions, built-in functions, built-in methods, methods, classes, class instances, generator functions, native coroutine functions, asynchronous generator functions.
Use
callable()
on an object to determine if it is callable.If a class defines a
__call__
method, then instances of the class can be called.To have keyword-only arguments in a function definition with variable positional arguments, specify the keyword-only arguments after the argument that captures the variable positional ones (the argument with a
*
).For keyword-only arguments in a function definition that doesn't have variable positional arguments, put a
*
by itself in the definition to separate the position from the keyword arguments.def new_func(name, *, location="Here", time)
Keyword-only arguments do not need to have a default value.
To define positional-only parameters, put a
\
in the parameter list. Arguments to the left of the\
are positional only. Other arguments may be added after the\
.