Python Advanced Topics: Properties, Descriptors, Metaclasses, Concurrency, and Built‑in Modules
This article explores advanced Python programming techniques, including using properties and descriptors to replace getters/setters, leveraging metaclasses for validation and registration, implementing concurrency with threads, subprocesses, queues, and coroutines, and utilizing built‑in modules such as functools, contextlib, copyreg, datetime, and decimal for robust development.
The article presents a collection of advanced Python programming guidelines, each introduced as a numbered "rule" that demonstrates best practices for writing clean, maintainable, and efficient code.
Rule 29: Replace getters and setters with simple public properties. Define class interfaces using public attributes and use @property when special behavior (e.g., validation) is required.
<code>class Homework:
def __init__(self):
self.__grade = 0
@property
def grade(self):
return self.__grade
@grade.setter
def grade(self, value):
if not (0 <= value <= 100):
raise ValueError('Grade must be between 0 and 100')
self.__grade = value
</code>Rule 30: Use @property to facilitate attribute refactoring. Adding new functionality to an existing attribute can be done without changing calling code, providing a smooth migration path.
Rule 31: Rewrite reusable @property methods with descriptors. Descriptors allow shared getter/setter logic across multiple classes while avoiding memory leaks when combined with weakref.WeakKeyDictionary .
<code>class Grade:
def __init__(self):
self.__value = weakref.WeakKeyDictionary()
def __get__(self, instance, owner):
return self.__value.get(instance, 0)
def __set__(self, instance, value):
if not (0 <= value <= 100):
raise ValueError('Grade must be between 0 and 100')
self.__value[instance] = value
class Exam:
math_grade = Grade()
chinese_grade = Grade()
science_grade = Grade()
</code>Rule 33–35: Employ metaclasses for class validation, registration, and annotation. A metaclass can enforce the presence of required attributes, automatically register subclasses in a global registry, and modify class attributes before the class body is executed.
<code>class Meta(type):
def __new__(meta, name, bases, class_dict):
if not class_dict.get('name'):
raise AttributeError('must have name attribute')
return type.__new__(meta, name, bases, class_dict)
class A(metaclass=Meta):
name = 'Example'
</code>Rule 36–41: Concurrency and parallelism strategies. Use the built‑in subprocess module for child processes, threading with Lock to protect shared data, Queue (or a custom ClosableQueue ) for pipeline coordination, and concurrent.futures for true parallel execution. Coroutines (generators with yield and send ) provide lightweight cooperative concurrency.
<code>import threading
from queue import Queue
class LockingCounter:
def __init__(self):
self.lock = threading.Lock()
self.count = 0
def increment(self, offset):
with self.lock:
self.count += offset
</code>Rule 42–44: Useful built‑in modules. functools.wraps preserves metadata when writing decorators; contextlib.contextmanager simplifies resource‑management code; copyreg enhances pickle for versioned objects.
<code>from functools import wraps
def my_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
print('Calling', func.__name__)
return func(*args, **kwargs)
return wrapper
</code>Rule 45–48: Standard library data structures and utilities. Use collections.deque , OrderedDict , defaultdict , heapq , bisect , and itertools for efficient algorithms. For precise arithmetic, prefer decimal.Decimal over floating‑point types.
Rule 49–53: Project organization. Write docstrings for all public objects, group modules into packages with a clear __all__ API, define package‑level exceptions, avoid circular imports by adjusting import order or using lazy imports, and isolate dependencies with virtual environments.
Rule 54–59: Deployment and diagnostics. Detect the runtime environment via sys and os , emit debugging information through repr , test code with unittest , debug interactively with pdb , profile performance using cProfile , and track memory usage with gc and tracemalloc .
The article concludes with a reminder to apply these practices to write clean, robust, and maintainable Python code.
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.