Coding challenge learning note (2)
This post is the continuation of the previous one. Topics covered here are: iterables, date, regex, read files, and other miscellaneous items.
🌀 About list, tuple, string
— 1. Tuple ordering and deep comparision in python.
— 2. string ljust()
method.
— 3. control the string split times using argument maxsplit
: string.split(sep, maxsplit=1)
.
— 4. concatenate tuple:
t = ()
for i in range(4):
t += (i,) # notice that we add , here
t
t = (5) # is still an int 5
t = [5] # is a list [5]
— 5. check if an item is iterable
# solution 1: use try/except
# if the for loop fails, then item is not iterable
try:
for x in item:
pass
except TypeError:
pass
# solution 2: use isinstance()
from collections.abc import Iterable
isinstance(item, Iterable)
# solution 3: use hasattr()
hasattr(item, '__iter__')
— 6. The dictionary setdefault
method will only set a key-value pair if the key isn’t in the dictionary yet. This can be useful if there are duplicate keys and we want the (key, value) pair that appears first.
dic = {}
for key, value in iterable:
dic.setdefault(key, value)
— 7. The shlex.split
function splits in a way that is quote-aware.
import shlex
shlex.split('This is a "quoted sentence"')
# ['This', 'is', 'a', 'quoted sentence']
# not quote-aware
'This is a "quoted sentence"'.split()
# ['This', 'is', 'a', '"quoted', 'sentence"']
🌀 About date
— 1. use monthcalendar
function from module calendar
to get a list of lists representing a calendar.
The Calendar class gives an object that represents a calendar with the first day of each week starting on the given weekday.
from datetime import date, timedelta
from calendar import monthcalendar, THURSDAY, Calendar
monthcalendar(2021, 6) #year, month
# [[0, 1, 2, 3, 4, 5, 6],
# [7, 8, 9, 10, 11, 12, 13],
# [14, 15, 16, 17, 18, 19, 20],
# [21, 22, 23, 24, 25, 26, 27],
# [28, 29, 30, 0, 0, 0, 0]]
thursday_calendar = Calendar(THURSDAY).monthdatescalendar(2021, 6)
🌀 About regex
— 1. Write good docstrings: semantic linefeeds.
below is an example of replacing every sentence ending character (.
, ?
, !
) which is followed by one or more spaces with that same character followed by a newline character.
import re
# use group capturing
re.sub(r'([.?!])[ ]+', r"\1\n", text)
# OR use positive lookbehind
re.sub(r'(?<=[.?!])[ ]+', r"\1\n", text)
# look for one or more spaces that are directly following a period question mark, or exclamation mark. The punctuation characters aren't actually matched here, just the spaces after them.
# if we want to handle double quotes outside of punctuation marks.
re.sub(r'([.?!]"?)[ ]+', r"\1\n", text)
# "? in the capture group indicates that " is a optional character
# We can't use a lookbehind because Python's regular expression engine requires that lookbehinds be a fixed number of characters.
🌀 About read file
— 1. Instead of using the open
function in a with
block, we could do use pathlib
module. The pathlib
module’s Path
object accepts a filename and creates an object which can be used in a number of ways. The read_text
method opens the file, reads the contents, closes the file, and returns the file contents.
from pathlib import Path
Path(file_name).read_text()
— 2. print message to standard error (sys.stderr
) instead of standard output, so that if someone piped the output to a file, they wouldn’t see this message printed to the file.
print(message, file=sys.stderr)
🌀 Other
— 1. make a namespace by creating a class:
class example:
attr_a = 's'
attr_b = 1
attr_c = [1, 2, 3]
example.attr_a # call the attribute
# if it's numeric attributes
from enum import IntEnum
from itertools import count
class example(IntEnum):
attr_a = 0
attr_b = 1
attr_c = 2
# OR
example = IntEnum('example', 'attr_a attr_b attr_c') # start count from 1
# OR
example = IntEnum('example', zip(['attr_a', 'attr_b', 'attr_c'], count()) # start count from 0
— 2. Why python does not have switch/case ?
— 3. The built-in round
function doesn’t always round upward at .5
customize to round-half-up:
# solution 1: use ceil
from math import ceil
if value % 1 >= 0.5:
value = ceil(value)
else:
value = floor(value)
# solution 2:
int(value + 0.5)
# solution 3:
from decimal import Decimal, ROUND_HALF_UP
value = Decimal(value).quantize(0, ROUND_HALF_UP)
— 4. programming style: EAFP (easier to ask forgiveness than permission), LBYL (look before you leap), duck typing
— 5. a better way to set default value for function arguments: use object()
default_value = object()
# default_value = None # warning: this will make None as a invalid default value)
def function(arg, default = default_value):
if default is not default_value:
pass
— 6. the functions below are two different ways to flatten an iterable of iterables (example taken from python morsel). The first one uses recursion, and the second function uses stack. It took me a long time to figure out the logic of the second function, and the key is to keep in mind that iterator is lazy and it can be looped only once! I think this is a very inspiring example so I put it here.
from collections.abc import Iterable
# use recursion
def deep_flatten(iterable):
for item in iterable:
if isinstance(item, Iterable):
yield from deep_flatten(item)
else:
yield item
# use stack
def deep_flatten(iterable):
iterators = [iter(iterable)] # tricky part is using iterator as elements in a list
while iterators:
for item in iterators[-1]:
if (isinstance(item, Iterable)
and not isinstance(item, (str, bytes))):
iterators.append(iter(item))
break
else:
yield item
else:
iterators.pop()
# example
t = [1, (2, 3)]
deep_flatten(t)
# 1, 2, 3