book website: https://effectivepython.com/
!python --version
Python 3.10.4
import sys
print(sys.version_info)
print(sys.version)
sys.version_info(major=3, minor=10, micro=4, releaselevel='final', serial=0) 3.10.4 (main, Jul 7 2022, 20:56:54) [Clang 13.1.6 (clang-1316.0.21.2.5)]
bytes
and str
¶bytes
contains sequences of 8-bit values, and str
contains sequences of Unicode code points.bytes
and str
instances can’t be used together with operators (like >, ==, +, and %).def to_str(bytes_or_str):
if isinstance(bytes_or_str, bytes):
value = bytes_or_str.decode('utf-8')
else:
value = bytes_or_str
return value
print(repr(to_str(b'foo')))
print(repr(to_str('bar')))
'foo' 'bar'
def to_bytes(bytes_or_str):
if isinstance(bytes_or_str, str):
value = bytes_or_str.encode('utf-8')
else:
value = bytes_or_str
return value
print(repr(to_bytes(b'foo')))
print(repr(to_bytes('bar')))
b'foo' b'bar'
!python -c 'import locale; print(locale.getpreferredencoding())'
UTF-8
str.format
¶places = 3
number = 1.23456
print(f'My number is {number:.{places}f}')
My number is 1.235
if
/else
expression provides a more readable alternative to using the Boolean operators or and and in expressions.from urllib.parse import parse_qs
my_values = parse_qs('red=5&blue=0&green=', keep_blank_values=True)
print(repr(my_values))
{'red': ['5'], 'blue': ['0'], 'green': ['']}
red = my_values.get('red', [''])[0] or 0
red
'5'
def get_first_int(values, key, default=0):
found = values.get(key, [''])
if found[0]:
return int(found[0])
return default
print(get_first_int(my_values, 'red'))
print(get_first_int(my_values, 'blue'))
print(get_first_int(my_values, 'green'))
print(get_first_int(my_values, 'yellow'))
5 0 0 0
snacks = [('bacon', 350), ('donut', 240), ('muffin', 190)]
for rank, (name, calories) in enumerate(snacks, 1):
print(f'#{rank}: {name} has {calories} calories')
#1: bacon has 350 calories #2: donut has 240 calories #3: muffin has 190 calories
enumerate
Over range
¶enumerate
provides concise syntax for looping over an iterator and getting the index of each item from the iterator as you go.enumerate
instead of looping over a range
and indexing into a sequence.enumerate
to specify the number from which to begin counting (zero is the default).”zip
to Process Iterators in Parallel¶zip
built-in function can be used to iterate over multiple iterators in parallel.zip
creates a lazy generator that produces tuples, so it can be used on infinitely long inputs.zip
truncates its output silently to the shortest iterator if you supply it with iterators of different lengths.zip_longest
function from the itertools
built-in module if you want to use zip
on iterators of unequal lengths without truncation.names = ['Cecilia', 'Lise', 'Marie']
counts = [len(n) for n in names]
longest_name = None
max_count = 0
for name, count in zip(names, counts):
if count > max_count:
longest_name = name
max_count = count
print(longest_name)
print(max_count)
Cecilia 7
names.append('Rosalind')
for name, count in zip(names, counts):
print(name)
Cecilia Lise Marie
import itertools
for name, count in itertools.zip_longest(names, counts):
print(f'{name}: {count}')
for name, count in itertools.zip_longest(names, counts, fillvalue=0):
print(f'{name}: {count}')
Cecilia: 7 Lise: 4 Marie: 5 Rosalind: None Cecilia: 7 Lise: 4 Marie: 5 Rosalind: 0
else
Blocks After for
and while
Loops¶else
blocks to immediately follow for
and while
loop interior blocks.else
block after a loop runs only if the loop body did not encounter a break
statement.else
blocks after loops because their behavior isn’t intuitive and can be confusing.a = 4
b = 9
for i in range(2, min(a, b) + 1):
print('Testing', i)
if a % i == 0 and b % i == 0:
print('Not coprime')
break
else:
print('Coprime')
Testing 2 Testing 3 Testing 4 Coprime
def coprime(a, b):
for i in range(2, min(a, b) + 1):
if a % i == 0 and b % i == 0:
return False
return True
assert coprime(4, 9)
assert not coprime(3, 6)
def coprime_alternate(a, b):
is_coprime = True
for i in range(2, min(a, b) + 1):
if a % i == 0 and b % i == 0:
is_coprime = False
break
return is_coprime
assert coprime_alternate(4, 9)
assert not coprime_alternate(3, 6)
fresh_fruit = {
'apple': 10,
'banana': 8,
'lemon': 5,
}
def make_lemonade(count):
pass
def out_of_stock():
pass
def slice_bananas(count):
pass
def make_cider(count):
pass
def make_smoothies(pieces):
pass
if count := fresh_fruit.get('lemon', 0):
make_lemonade(count)
else:
out_of_stock()
if (count := fresh_fruit.get('banana', 0)) >= 2:
pieces = slice_bananas(count)
to_enjoy = make_smoothies(pieces)
elif (count := fresh_fruit.get('apple', 0)) >= 4:
to_enjoy = make_cider(count)
elif count := fresh_fruit.get('lemon', 0):
to_enjoy = make_lemonade(count)
else:
to_enjoy = 'Nothing'
key
Parameter¶dict
Insertion Ordering¶get
Over in
and KeyError to Handle Missing Dictionary Keys¶defaultdict
Over setdefault
to Handle Missing Items in Internal State¶__missing__
¶None
¶None
and Docstrings to Specify Dynamic Default Arguments¶functools.wraps
¶map
and filter
¶yield from
¶send
¶throw
¶itertools
for Working with Iterators and Generators¶@classmethod
Polymorphism to Construct Objects Generically¶super
¶collections.abc
for Custom Container Types¶@property
Instead of Refactoring Attributes¶@property
Methods¶__getattr__
, __getattribute__
, and __setattr__
for Lazy Attributes¶__init_subclass__
¶__init_subclass__
¶__set_name__
¶subprocess
to Manage Child Processes¶ThreadPoolExecuter
When Threads Are Necessary for Concurrency¶asyncio
¶asyncio
¶asyncio
Event Loop to Maximize Responsiveness¶concurrent.futures
for True Parallelism¶try
/except
/else
/finally
¶contextlib
and with
Statements for Reusable try
/finally
Behavior¶datetime
Instead of time
for Local Clocks¶pickle
Reliable with copyreg
¶decimal
When Precision Is Paramount¶deque
for Producer-Consumer Queues¶bisect
¶heapq
for Priority Queues¶memoryview
and bytearray
for Zero-Copy Interactions with bytes
¶repr
Strings for Debugging Output¶pdb
¶tracemalloc
to Understand Memory Usage and Leaks¶warnings
to Refactor and Migrate Usage¶typing
to Obviate Bugs¶