Python Essentials: Strings, Collections, Iterators, and Decorators
This article presents a comprehensive collection of Python fundamentals, covering useful string methods like partition and translate, mutable versus immutable types and argument passing, tuple unpacking, dictionary utilities, set operations, iterator implementations, best practices for exception handling, and various decorator techniques with practical code examples.
The author, seeking to improve Python project structure, read the book "Python Craftsman" and extracted a series of useful snippets covering many core language features.
Uncommon but handy string methods : the partition method splits a string into three parts (before, separator, after) and can replace a multi‑line split implementation with a single line. Example:
<code>def extract_value(s):
return s.partition(':')[2]</code>The translate method performs bulk character replacement efficiently:
<code>s = "Hello, World!"
table = str.maketrans(",!", ".~")
print(s.translate(table)) # Hello. World~</code>Long multi‑line strings can have their indentation cleaned using textwrap.dedent :
<code>from textwrap import dedent
def welcome():
return dedent("""Welcome to my website.
- I'm a Python developer.
- I love coding.
""")</code>Defining enumerations with the built‑in enum module:
<code>from enum import Enum
class Color(int, Enum):
RED = 1
GREEN = 2
BLUE = 3
color = Color.RED
print(1 == color) # True</code>Mutable vs. immutable types and function argument passing : Python passes arguments by object reference. Modifying an immutable argument creates a new object, while modifying a mutable argument alters the original. Illustrations:
<code>def add_string(s):
s += 'world'
return s
s = 'hello'
print(add_string(s)) # helloworld
print(s) # hello
def add_list(l):
l.append('world')
return l
l = ['hello']
print(add_list(l)) # ['hello', 'world']
print(l) # ['hello', 'world']</code>Functions can return multiple values as a tuple, which can be unpacked:
<code>def get_name_and_age():
return 'John', 30
result = get_name_and_age()
print(type(result)) # <class 'tuple'>
</code>Named tuples improve readability:
<code>from collections import namedtuple
Rectangle = namedtuple('Rectangle', ['width', 'height'])
rect = Rectangle(10, 20)
print(rect.width, rect.height)
</code>Dictionary utilities : get provides a default for missing keys, setdefault inserts a value when the key is absent, and pop can return a default instead of raising KeyError :
<code>d = {}
# using setdefault
d.setdefault('item', []).append('new_value')
</code>Set operations such as difference, union, and intersection are demonstrated:
<code>s1 = {1, 2, 3, 4}
s2 = {3, 4, 5, 6}
print(s1 - s2) # {1, 2}
print(s1 | s2) # {1, 2, 3, 4, 5, 6}
print(s1 & s2) # {3, 4}
</code>Merging dictionaries can be done with unpacking or the | operator:
<code>d1 = {'a': 1, 'b': 2}
d2 = {'c': 3, 'd': 4}
d3 = {**d1, **d2}
d4 = d1 | d2
</code>Exception handling best practices : place more specific except clauses before generic ones, use try...else to separate successful execution logic, and avoid catching overly broad exceptions.
<code>def incr_by_key(d, key):
try:
d[key] += 1
except KeyError as e:
print(e)
</code>Iterables and iterators : an iterator implements both __iter__ and __next__ . A simple range‑like iterator can be written as:
<code>class MyRange:
def __init__(self, start, end):
self.start = start
self.end = end
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current < self.end:
cur = self.current
self.current += 1
return cur
raise StopIteration
</code>To make the object reusable, separate the iterable from the iterator:
<code>class MyRange:
def __init__(self, start, end):
self.start = start
self.end = end
def __iter__(self):
return MyRangeIterator(self)
class MyRangeIterator:
def __init__(self, my_range):
self.my_range = my_range
self.current = my_range.start
def __next__(self):
if self.current < self.my_range.end:
cur = self.current
self.current += 1
return cur
raise StopIteration
def __iter__(self):
return self
</code>A generator using yield provides an even shorter implementation:
<code>def my_range(start, end):
while start < end:
yield start
start += 1
</code>Function default mutable arguments are a common pitfall; using a mutable default leads to shared state across calls:
<code>def f(a, L=[]):
L.append(a)
return L
print(f(1)) # [1]
print(f(2)) # [1, 2]
</code>Keyword‑only arguments are enforced by placing a * in the signature:
<code>def f(a, *, b):
print(a, b)
f(1, b=2)
</code>Decorators : using functools.wraps preserves the original function’s metadata. An example timing decorator:
<code>def time_it(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
ret = func(*args, **kwargs)
end = time.time()
print(f'{func.__name__} took {end - start} seconds')
return ret
return wrapper
</code>Optional parameters can be added by returning a decorator factory, and a class can be made callable to act as a decorator:
<code>class Timer:
def __init__(self, unit='s'):
self.unit = unit
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
ret = func(*args, **kwargs)
end = time.time()
if self.unit == 's':
print(f'{func.__name__} took {end - start} seconds')
else:
print(f'{func.__name__} took {(end - start) * 1000} ms')
return ret
return wrapper
</code>The article concludes with a promotional QR code for a free Python public course, but the technical content above provides a solid reference for Python developers.
Python Programming Learning Circle
A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.