跳转至

修饰器

在 Python 中,修饰器(decorators)是用于修改函数或类方法行为的特殊函数。常用修饰器主要有以下几类:

0. @property

  • 作用:将类中的方法转化为属性,使得你可以像访问属性一样调用方法,而不需要使用括号 ().
  • 实现效果:简化属性访问,隐藏内部实现细节。
  • 示例

    class Person:
        def __init__(self, name):
            self._name = name
    
        @property
        def name(self):
            return self._name
    

  • 调用person.name 而不是 person.name()

1. @property. setter

  • 作用:允许你对该属性进行赋值操作。如果没有 setter 方法,属性是只读的。
  • 示例
    @age.setter
    def age(self, age):
        self._age = age
    

只有这个修饰了,才能修改这个值,否则会出现 AttrubiteError 错误

2. @staticmethod

  • 作用:将类中的方法声明为静态方法,不需要通过实例化类来调用方法,也没有对类实例的依赖。
  • 实现效果:适合那些不需要访问类或实例属性的方法。
  • 示例

    class Math:
        @staticmethod
        def add(a, b):
            return a + b
    

  • 调用Math.add(5, 3)

3. @classmethod

  • 作用:定义一个类方法,第一个参数 cls 代表类本身,可以通过类调用,也可以通过实例调用。
  • 实现效果:允许修改类的状态或调用类方法。
  • 示例

    class Person:
        _count = 0
    
        def __init__(self, name):
            self.name = name
            Person._count += 1
    
        @classmethod
        def get_count(cls):
            return cls._count
    

  • 调用Person.get_count() 或者 person.get_count()

4. @staticmethod vs @classmethod

  • @staticmethod 不涉及类的状态,也不接收类实例或类本身作为参数。
  • @classmethod 可以操作类级别的状态。

5. @lru_cache (functools. lru_cache)

  • 作用:缓存函数的返回结果,以提高重复调用时的效率(通常用于递归或密集计算)。
  • 实现效果:缓存机制,减少重复计算。
  • 示例
    from functools import lru_cache
    
    @lru_cache(maxsize=None)
    def fib(n):
        if n < 2:
            return n
        return fib(n-1) + fib(n-2)
    

6. @wraps (functools. wraps)

  • 作用:用于装饰器函数内部,确保装饰器不会改变原函数的元数据(如函数名、文档字符串等)。
  • 实现效果:保持被装饰函数的原有属性。
  • 示例
    from functools import wraps
    
    def my_decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            print("Before function call")
            result = func(*args, **kwargs)
            print("After function call")
            return result
        return wrapper
    

7. @abstractmethod (abc. abstractmethod)

  • 作用:用于抽象类中,要求子类必须实现该方法。
  • 实现效果:强制子类提供特定方法的实现。
  • 示例
    from abc import ABC, abstractmethod
    
    class Animal(ABC):
        @abstractmethod
        def sound(self):
            pass
    
    class Dog(Animal):
        def sound(self):
            return "Woof"
    

这些是 Python 中常见且有用的修饰器。通过它们,可以更加简洁和灵活地控制类和函数的行为。


机制

__slots__ 不是一个修饰器,而是 Python 中类的一种机制,用于限制类实例的属性。它可以帮助节省内存,并提高某些情况下的性能。下面详细解释其作用和使用场景。

1. 什么是 __slots__

通常,Python 中的类实例会将其属性存储在一个名为 __dict__ 的字典中。每次你为类实例添加新属性时,都会在 __dict__ 中添加一个键值对。然而,字典是比较消耗内存的结构,尤其是当你有大量对象实例时。

__slots__ 通过限制类实例只能有特定的属性,避免了为每个实例创建 __dict__,从而节省内存。

2. 使用 __slots__ 的效果

  • 限制类实例只能有指定的属性。
  • 减少内存使用。
  • 禁止动态添加新的属性(如果不在 __slots__ 中定义)。

3. 示例

class Person:
    __slots__ = ['name', 'age']  # 限制只能有 'name' 和 'age' 这两个属性

    def __init__(self, name, age):
        self.name = name
        self.age = age

# 创建一个对象
p = Person('Alice', 30)

# 访问属性
print(p.name)  # 输出: Alice
print(p.age)   # 输出: 30

# 尝试添加新属性会导致错误
p.address = 'New York'  # AttributeError: 'Person' object has no attribute 'address'

4. 优点

  • 内存节省:当有大量实例时,使用 __slots__ 可以显著减少内存消耗。因为每个对象不再有 __dict__ 存储属性。

  • 性能优化:访问和设置属性的速度可能会提高,因为它绕过了 __dict__ 的键值查找过程。

5. 注意事项

  • 不适合所有类:如果你需要动态添加属性,或者不确定类的所有属性,可以不使用 __slots__
  • 继承的限制:在子类中,__slots__ 的行为可能会带来复杂性。如果子类没有定义 __slots__,它将拥有自己的 __dict__

  • 无法使用 __dict____weakref__:当你使用 __slots__ 时,类实例将不再有 __dict____weakref__,除非你明确包含它们:

    class Person:
        __slots__ = ['name', 'age', '__dict__', '__weakref__']
    

6. 适用场景

  • 大量实例化对象:例如在需要创建数百万个对象实例时,使用 __slots__ 可以显著减少内存占用。
  • 固定属性的类:适合那些属性集合固定的类,避免意外地添加新的属性。

总结

__slots__ 是一个用于优化 Python 对象内存使用的工具。它通过限制类实例的属性并取消 __dict__ 的创建来节省空间,并可以提高访问速度。但在灵活性上有所妥协,因此应在内存优化需求较高的场景下使用。


1. __new____init__

  • __new__:负责创建对象实例。__new__ 是一个静态方法,在实例化时优先调用,返回一个新的实例。
  • __init__:负责初始化实例属性,__new__ 创建对象后会调用 __init__
  • 使用场景:当需要控制对象创建过程(如实现单例模式)时使用 __new__
  • 示例
    class MyClass:
        def __new__(cls, *args, **kwargs):
            print("Creating instance")
            return super().__new__(cls)
    
        def __init__(self, name):
            self.name = name
            print("Initializing instance")
    

2. __repr____str__

  • __repr__:定义对象的“官方”字符串表示,主要用于调试和开发。
  • __str__:定义对象的“友好”字符串表示,主要用于用户端展示。
  • 区别__repr__ 应该尽可能返回一个可以用来重建对象的字符串;__str__ 更关注可读性。
  • 示例
    class MyClass:
        def __repr__(self):
            return f"MyClass({self.name})"
    
        def __str__(self):
            return f"Name is {self.name}"
    

3. 上下文管理器 (with 语句) 和 __enter__, __exit__

  • 作用with 语句用于简化资源管理(如文件操作、数据库连接),自动处理资源的获取和释放。
  • __enter__:在 with 块开始时执行,返回资源对象。
  • __exit__:在 with 块结束时执行,负责清理资源(如关闭文件、释放锁)。
  • 示例
    class MyContext:
        def __enter__(self):
            print("Entering context")
            return self
    
        def __exit__(self, exc_type, exc_val, exc_tb):
            print("Exiting context")
    
    with MyContext():
        print("Inside context")
    

4. 迭代器和生成器

  • 迭代器 (__iter____next__):定义可迭代对象,__iter__ 返回迭代器对象,__next__ 返回下一个元素。
  • 生成器 (yield):通过 yield 关键字生成惰性计算序列,每次调用 next() 时生成下一个值。
  • 使用场景:处理大型数据集时,生成器可以节省内存;迭代器可用于自定义迭代行为。
  • 示例
    def my_generator():
        yield 1
        yield 2
        yield 3
    
    gen = my_generator()
    print(next(gen))  # 输出: 1
    

5. 元类 (metaclass)

  • 作用:元类是用于创建类的“类”,它决定了类的创建方式。可以通过自定义元类来控制类的行为或属性。
  • 使用场景:当需要动态控制类的创建或修改类的行为时使用。
  • 示例
    class MyMeta(type):
        def __new__(cls, name, bases, dct):
            print(f"Creating class {name}")
            return super().__new__(cls, name, bases, dct)
    
    class MyClass(metaclass=MyMeta):
        pass
    

6. 反射 (getattr, setattr, hasattr)

  • 作用:允许动态访问对象的属性或方法,通过名称在运行时获取或修改属性。
  • 使用场景:在不确定对象结构时,通过字符串动态访问对象的属性或方法。
  • 示例
    class MyClass:
        def __init__(self, name):
            self.name = name
    
    obj = MyClass("Alice")
    print(getattr(obj, "name"))  # 输出: Alice
    setattr(obj, "name", "Bob")
    print(obj.name)  # 输出: Bob
    

7. 装饰器 (decorators)

  • 作用:装饰器是用于包装函数或类的函数,允许在不修改原函数或类的前提下增强其功能。
  • 使用场景:如日志记录、权限验证、缓存、性能优化等。
  • 示例
    def my_decorator(func):
        def wrapper(*args, **kwargs):
            print("Before function call")
            result = func(*args, **kwargs)
            print("After function call")
            return result
        return wrapper
    
    @my_decorator
    def say_hello():
        print("Hello")
    
    say_hello()
    

8. 运算符重载 (__add__, __mul__, 等)

  • 作用:允许自定义类的运算符行为(如 +, * 等),通过定义特定的魔法方法来改变这些运算符对类实例的操作。
  • 示例
    class Point:
        def __init__(self, x, y):
            self.x = x
            self.y = y
    
        def __add__(self, other):
            return Point(self.x + other.x, self.y + other.y)
    
    p1 = Point(1, 2)
    p2 = Point(3, 4)
    result = p1 + p2
    print(result.x, result.y)  # 输出: 4 6
    

这些机制在 Python 中非常常用,它们让代码更具表现力、灵活性和可扩展性。