Day 29 - @staticmethod 装饰器与单例模式
什么是静态方法?
静态方法(Static Method)是使用 @staticmethod 装饰器装饰的方法。它不需要 self 或 cls 参数,可以像普通函数一样定义,但在类内部定义,属于类的命名空间。
静态方法的主要用途:
- 将一些与类相关的功能封装在类内部,使代码更组织化
- 不需要访问类属性或实例属性的功能
- 可以被子类继承(但不能通过
super()调用父类的静态方法)
class MathUtils:
"""数学工具类"""
@staticmethod
def add(a, b):
"""加法"""
return a + b
@staticmethod
def multiply(a, b):
"""乘法"""
return a * b
@staticmethod
def is_prime(n):
"""判断素数"""
if n < 2:
return False
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
return False
return True
# 调用静态方法
print(MathUtils.add(3, 5)) # 8
print(MathUtils.multiply(4, 7)) # 28
print(MathUtils.is_prime(17)) # True
print(MathUtils.is_prime(15)) # False
# 也可以通过实例调用
utils = MathUtils()
print(utils.add(2, 3)) # 5
三种方法的对比
class Example:
class_attr = "类属性"
def __init__(self, value):
self.value = value
def instance_method(self):
"""实例方法:需要 self"""
return f"实例方法,value={self.value}"
@classmethod
def class_method(cls):
"""类方法:需要 cls"""
return f"类方法,class_attr={cls.class_attr}"
@staticmethod
def static_method():
"""静态方法:不需要 self 或 cls"""
return "静态方法,不依赖类或实例"
# 对比
obj = Example("test")
# 实例方法:需要实例调用
print(obj.instance_method())
# Example.instance_method(obj) # 需要显式传递实例
# 类方法:类或实例都可以调用
print(Example.class_method())
print(obj.class_method())
# 静态方法:类或实例都可以调用
print(Example.static_method())
print(obj.static_method())
静态方法与命名空间
静态方法的一个重要作用是将相关的函数组织在类的命名空间中,避免污染全局命名空间。
import math
# 不推荐:所有函数都在全局命名空间
def calculate_circle_area(r):
return math.pi * r ** 2
def calculate_circle_circumference(r):
return 2 * math.pi * r
# 推荐:将相关函数组织在类中
class Circle:
def __init__(self, radius):
self.radius = radius
@staticmethod
def area(radius):
"""计算圆面积"""
return math.pi * radius ** 2
@staticmethod
def circumference(radius):
"""计算圆周长"""
return 2 * math.pi * radius
def total_area(self, other):
"""计算两个圆的总面积"""
return Circle.area(self.radius) + Circle.area(other.radius)
c1 = Circle(5)
c2 = Circle(3)
# 静态方法可以直接通过类调用
print(Circle.area(5)) # 78.54
print(Circle.circumference(5)) # 31.42
# 也可以通过实例调用
print(c1.area(5)) # 78.54
# 在实例方法中调用静态方法
print(c1.total_area(c2)) # 98.96
静态方法与继承
静态方法可以被继承,但调用方式与类方法不同。
class Base:
@staticmethod
def static_method():
print("Base.static_method")
class Derived(Base):
@staticmethod
def static_method():
print("Derived.static_method")
# 测试
Base.static_method() # Base.static_method
Derived.static_method() # Derived.static_method
base = Base()
base.static_method() # Base.static_method
derived = Derived()
derived.static_method() # Derived.static_method
# 通过子类调用父类的静态方法(不推荐)
base.static_method() # 仍然是 Base 的版本
单例模式详解
单例模式(Singleton Pattern)是一种设计模式,确保一个类只有一个实例,并提供一个全局访问点。
方法1:使用 __new__
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._initialized = False
return cls._instance
def __init__(self):
if not self._initialized:
self.data = None
Singleton._instance._initialized = True
def set_data(self, data):
self.data = data
def get_data(self):
return self.data
# 测试
s1 = Singleton()
s2 = Singleton()
print(s1 is s2) # True
s1.set_data(42)
print(s2.get_data()) # 42
方法2:使用装饰器
def singleton(cls):
"""单例装饰器"""
instances = {}
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class Database:
def __init__(self, host, port):
self.host = host
self.port = port
print(f"连接数据库:{host}:{port}")
# 测试
db1 = Database("localhost", 3306)
db2 = Database("remote", 5432)
print(db1 is db2) # True
print(db1.host) # localhost
print(db2.host) # localhost(来自第一次创建)
方法3:使用元类(Metaclass)
class SingletonMeta(type):
"""单例元类"""
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class Database(metaclass=SingletonMeta):
def __init__(self, host, port):
self.host = host
self.port = port
def query(self, sql):
print(f"执行查询:{sql}")
# 测试
db1 = Database("localhost", 3306)
db2 = Database("remote", 5432)
print(db1 is db2) # True
db1.query("SELECT * FROM users")
方法4:使用模块
Python 的模块本身就是单例,因为模块只会被导入一次。
# my_singleton.py
class Singleton:
def __init__(self):
self.data = None
_instance = Singleton()
def get_instance():
return _instance
# 在其他文件中使用
# from my_singleton import get_instance
# db = get_instance()
单例模式的线程安全
基础的单例实现在多线程环境下可能有问题。使用线程锁可以解决这个问题。
import threading
class ThreadSafeSingleton:
_instance = None
_lock = threading.Lock()
def __new__(cls, *args, **kwargs):
if cls._instance is None:
with cls._lock:
# 双重检查锁定
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._initialized = False
return cls._instance
def __init__(self, value=None):
if not self._initialized:
self.value = value
ThreadSafeSingleton._instance._initialized = True
# 测试多线程创建
def create_singleton():
s = ThreadSafeSingleton("test")
print(f"线程创建的实例:{id(s)}, value={s.value}")
threads = [threading.Thread(target=create_singleton) for _ in range(5)]
for t in threads:
t.start()
for t in threads:
t.join()
Borg 模式(共享状态单例)
Borg 模式不是让所有实例都是同一个对象,而是让所有实例共享同一个状态。
class Borg:
"""Borg 模式:所有实例共享同一个状态"""
_shared_state = {}
def __init__(self):
self.__dict__ = self._shared_state
if not hasattr(self, 'initialized'):
self.initialized = True
self.data = None
def set_data(self, data):
self.data = data
def get_data(self):
return self.data
# 测试
b1 = Borg()
b2 = Borg()
print(b1 is b2) # False(不同的对象)
b1.set_data(42)
print(b2.get_data()) # 42(共享状态)
b2.data = "shared"
print(b1.data) # shared
静态方法在实际项目中的应用
示例1:验证器
class Validator:
"""数据验证器"""
@staticmethod
def is_valid_email(email):
import re
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return re.match(pattern, email) is not None
@staticmethod
def is_valid_phone(phone):
import re
pattern = r'^1[3-9]\d{9}$'
return re.match(pattern, phone) is not None
@staticmethod
def is_valid_id_card(id_card):
import re
pattern = r'^\d{17}[\dXx]$'
return re.match(pattern, id_card) is not None
@staticmethod
def validate_range(value, min_val, max_val):
"""验证值是否在指定范围内"""
return min_val <= value <= max_val
# 使用
print(Validator.is_valid_email("user@example.com")) # True
print(Validator.is_valid_email("invalid-email")) # False
print(Validator.is_valid_phone("13800138000")) # True
print(Validator.is_valid_id_card("11010119900101123X")) # True
print(Validator.validate_range(5, 1, 10)) # True
示例2:转换工具
class Converter:
"""数据转换工具"""
@staticmethod
def celsius_to_fahrenheit(c):
return c * 9/5 + 32
@staticmethod
def fahrenheit_to_celsius(f):
return (f - 32) * 5/9
@staticmethod
def meters_to_feet(m):
return m * 3.28084
@staticmethod
def feet_to_meters(ft):
return ft / 3.28084
@staticmethod
def kg_to_pounds(kg):
return kg * 2.20462
@staticmethod
def pounds_to_kg(lb):
return lb / 2.20462
# 使用
print(f"25°C = {Converter.celsius_to_fahrenheit(25):.2f}°F")
print(f"77°F = {Converter.fahrenheit_to_celsius(77):.2f}°C")
print(f"100m = {Converter.meters_to_feet(100):.2f}ft")
print(f"328ft = {Converter.feet_to_meters(328):.2f}m")
示例3:配置常量
class AppConfig:
"""应用配置常量"""
DEBUG = False
TESTING = False
PRODUCTION = True
# 数据库配置
DB_HOST = "localhost"
DB_PORT = 3306
DB_NAME = "myapp"
# API 配置
API_VERSION = "v1"
API_TIMEOUT = 30
@staticmethod
def is_production():
return AppConfig.PRODUCTION
@staticmethod
def is_debug():
return AppConfig.DEBUG
@classmethod
def get_db_url(cls):
"""获取数据库 URL"""
return f"mysql://{cls.DB_HOST}:{cls.DB_PORT}/{cls.DB_NAME}"
# 使用
print(f"运行环境:{'生产' if AppConfig.is_production() else '开发'}")
print(f"数据库URL:{AppConfig.get_db_url()}")
练习题
练习 1:实现字符串工具类
class StringUtils:
"""字符串工具类"""
@staticmethod
def reverse(s):
"""反转字符串"""
return s[::-1]
@staticmethod
def is_palindrome(s):
"""判断回文数"""
s = ''.join(c.lower() for c in s if c.isalnum())
return s == s[::-1]
@staticmethod
def count_vowels(s):
"""统计元音字母数量"""
vowels = set('aeiouAEIOU')
return sum(1 for c in s if c in vowels)
@staticmethod
def remove_whitespace(s):
"""移除所有空白字符"""
return ''.join(c for c in s if not c.isspace())
@staticmethod
def truncate(s, length, suffix="..."):
"""截断字符串"""
if len(s) <= length:
return s
return s[:length - len(suffix)] + suffix
# 测试
print(StringUtils.reverse("hello")) # olleh
print(StringUtils.is_palindrome("A man a plan a canal Panama")) # True
print(StringUtils.is_palindrome("hello")) # False
print(StringUtils.count_vowels("Hello World")) # 3
print(StringUtils.remove_whitespace(" a b c ")) # abc
print(StringUtils.truncate("Hello, World!", 8)) # Hello...
练习 2:实现简单的缓存(单例)
class Cache:
"""简单的内存缓存(单例)"""
_instance = None
_lock = threading.Lock()
def __new__(cls):
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self):
if not hasattr(self, '_data'):
self._data = {}
@staticmethod
def get(key, default=None):
"""获取缓存值"""
instance = Cache()
return instance._data.get(key, default)
@staticmethod
def set(key, value):
"""设置缓存值"""
instance = Cache()
instance._data[key] = value
@staticmethod
def delete(key):
"""删除缓存值"""
instance = Cache()
if key in instance._data:
del instance._data[key]
@staticmethod
def clear():
"""清空缓存"""
instance = Cache()
instance._data.clear()
@staticmethod
def keys():
"""获取所有缓存键"""
instance = Cache()
return list(instance._data.keys())
import threading
# 测试
Cache.set("name", "张三")
Cache.set("age", 25)
print(Cache.get("name")) # 张三
print(Cache.get("age")) # 25
print(Cache.keys()) # ['name', 'age']
Cache.delete("age")
print(Cache.keys()) # ['name']
# 多线程测试
def worker():
for i in range(10):
Cache.set(f"key_{threading.current_thread().name}_{i}", i*i)
threads = [threading.Thread(target=worker, name=f"T{i}") for i in range(3)]
for t in threads:
t.start()
for t in threads:
t.join()
print(f"缓存条目数:{len(Cache.keys())}") # 应该是 30
练习 3:实现类工厂
class Animal:
"""动物基类"""
pass
class AnimalFactory:
"""动物工厂"""
_registry = {}
@classmethod
def register(cls, animal_type, animal_class):
"""注册动物类"""
cls._registry[animal_type] = animal_class
@classmethod
def create(cls, animal_type, *args, **kwargs):
"""创建动物实例"""
if animal_type not in cls._registry:
raise ValueError(f"未知的动物类型:{animal_type}")
return cls._registry[animal_type](*args, **kwargs)
@staticmethod
def list_types():
"""列出所有已注册的动物类型"""
return list(AnimalFactory._registry.keys())
class Dog(Animal):
def __init__(self, name):
self.name = name
def speak(self):
return "汪汪汪"
class Cat(Animal):
def __init__(self, name):
self.name = name
def speak(self):
return "喵喵喵"
# 注册动物类型
AnimalFactory.register("dog", Dog)
AnimalFactory.register("cat", Cat)
# 创建动物实例
dog = AnimalFactory.create("dog", "旺财")
cat = AnimalFactory.create("cat", "小白")
print(f"{dog.name}:{dog.speak()}") # 旺财:汪汪汪
print(f"{cat.name}:{cat.speak()}") # 小白:喵喵喵
print(f"已注册的动物类型:{AnimalFactory.list_types()}") # ['dog', 'cat']
总结
@staticmethod 和单例模式是 Python 面向对象编程的重要内容:
- 静态方法:不需要
self或cls的方法,用于组织相关功能 - 与类方法的对比:静态方法不能访问类属性或实例属性
- 单例模式:确保类只有一个实例
- 实现方式:
__new__、装饰器、元类、模块 - 线程安全:多线程环境下需要注意线程安全
- Borg 模式:共享状态的单例变体
在下一节中,我们将学习异常处理:try-except-else-finally。