Meta Llama 3 里面装饰器

发布于:2024-06-11 ⋅ 阅读:(50) ⋅ 点赞:(0)

Meta Llama 3 里面的装饰器

flyfish

介绍

@ 符号在 Python 中用于装饰器语法糖,提供了一种简洁而强大的方式来修改或扩展函数和方法的行为。

Meta Llama 3 里面有
@staticmethod
@dataclass
@torch.inference_mode()

@staticmethod静态方法属于类,而不是类的实例。可以在不创建类的实例的情况下调用静态方法。

@staticmethod

在 Python 中,@staticmethod 是一个装饰器,用于定义静态方法。静态方法属于类,而不是类的实例。也就是说,可以在不创建类的实例的情况下调用静态方法。静态方法通常用于实现与类相关,但不依赖于实例状态的方法。

静态方法有以下特点:

  1. 不需要访问实例或类本身。
  2. 不接收隐含的第一个参数(通常是 self 或 cls)。
  3. 可以通过类名直接调用。

使用 @staticmethod 的示例

假设我们有一个类 MathOperations,它包含一些数学运算的方法。某些运算,比如计算两个数的和,可以独立于实例存在。我们可以将这样的函数定义为静态方法。

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

    @staticmethod
    def multiply(a, b):
        return a * b
    
# 调用静态方法
result_add = MathOperations.add(3, 5)
result_multiply = MathOperations.multiply(4, 6)

print(f"Addition Result: {result_add}")          # 输出: Addition Result: 8
print(f"Multiplication Result: {result_multiply}") # 输出: Multiplication Result: 24

在这个例子中:

  • MathOperations 类定义了两个静态方法 add 和 multiply。
  • 这些方法可以直接通过类名调用,而无需创建类的实例。
    静态方法对于那些不依赖于实例状态的方法非常有用,它们可以保持代码的清晰和组织性。

何时使用静态方法

静态方法适用于以下情况:

  • 方法逻辑与类紧密相关,但不需要访问或修改类或实例的状态。
  • 逻辑可以在类级别实现,而不依赖于类的实例。

另一个示例:日期处理

class DateUtils:
    
    @staticmethod
    def is_leap_year(year):
        if (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0):
            return True
        else:
            return False
    
    @staticmethod
    def days_in_month(month, year):
        days_in_month = [31, 29 if DateUtils.is_leap_year(year) else 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
        return days_in_month[month - 1]

# 调用静态方法
print(DateUtils.is_leap_year(2020))  # 输出: True
print(DateUtils.days_in_month(2, 2020))  # 输出: 29
print(DateUtils.days_in_month(2, 2021))  # 输出: 28

在这个例子中:

  • DateUtils 类定义了两个静态方法 is_leap_year 和 days_in_month。
  • 这些方法提供了一些日期处理的功能,可以直接通过类名调用。

其他的内置装饰器

顺便介绍
@property:用于将方法转换为属性,允许以属性的方式访问和设置方法的值。
@contextmanager:用于创建上下文管理器,简化资源管理。
@classmethod:用于定义类方法,允许在类本身而不是实例上调用方法。

@property

@property 装饰器用于将类的方法转换为属性。它允许你使用类似于访问属性的语法来调用方法。这对于封装对象的内部数据并控制其访问和修改非常有用。

示例

class Circle:
    def __init__(self, radius):
        self._radius = radius

    @property
    def radius(self):
        return self._radius

    @radius.setter
    def radius(self, value):
        if value < 0:
            raise ValueError("Radius cannot be negative")
        self._radius = value

    @property
    def area(self):
        return 3.14159 * self._radius ** 2

# 使用属性
circle = Circle(5)
print(circle.radius)  # 输出: 5
print(circle.area)    # 输出: 78.53975

circle.radius = 10
print(circle.area)    # 输出: 314.159

try:
    circle.radius = -5  # 将引发 ValueError
except ValueError as e:
    print(e)  # 输出: Radius cannot be negative

在这个示例中,radius 和 area 都是属性。通过 @property 和 @radius.setter,我们能够控制对 _radius 属性的访问和修改。

@contextmanager

@contextmanager 是 contextlib 模块中的一个装饰器,用于简化上下文管理器的创建。它允许你使用 with 语句,提供一种干净的方式来管理资源(如文件、网络连接等)。

示例
from contextlib import contextmanager

@contextmanager
def open_file(file_name, mode):
    f = open(file_name, mode)
    try:
        yield f
    finally:
        f.close()

# 使用上下文管理器
with open_file('test.txt', 'w') as f:
    f.write('Hello, World!')

# 自动关闭文件

在这个示例中,open_file 函数使用 @contextmanager 装饰器将其转换为一个上下文管理器。with 语句在进入和退出时分别调用 yield 之前和之后的代码,从而确保文件在使用后被自动关闭。

@classmethod

@classmethod 装饰器用于定义类方法。类方法的第一个参数是类本身(通常命名为 cls),而不是实例。类方法可以用于创建工厂方法或执行需要访问类本身而不是实例的操作。

示例

class MyClass:
    class_variable = 0

    def __init__(self, instance_variable):
        self.instance_variable = instance_variable

    @classmethod
    def increment_class_variable(cls):
        cls.class_variable += 1
        return cls.class_variable

# 使用类方法
print(MyClass.class_variable)  # 输出: 0

MyClass.increment_class_variable()
print(MyClass.class_variable)  # 输出: 1

instance = MyClass(10)
print(instance.increment_class_variable())  # 输出: 2

在这个示例中,increment_class_variable 是一个类方法,它操作类变量 class_variable。通过 @classmethod 装饰器,我们可以在不创建类实例的情况下调用该方法。

@classmethod 与 @staticmethod 的比较

  1. 第一个参数是类本身 (cls):
  • @classmethod 装饰的方法的第一个参数是类本身,通常命名为 cls。通过这个参数,可以访问和修改类的状态。
  1. 可以通过类或实例调用:
  • 类方法可以通过类名或实例调用,效果是相同的。
  1. 常用于工厂方法和对类变量的操作:
  • 类方法常用于创建类的实例(工厂方法)或操作类变量。

@staticmethod

  1. 没有默认的第一个参数:
  • @staticmethod 装饰的方法没有默认的第一个参数(例如 self 或 cls)。它完全独立于类和实例。
  1. 可以通过类或实例调用:
  • 静态方法可以通过类名或实例调用,但它们无法访问类或实例的任何属性或方法。
  1. 常用于不需要访问类或实例的逻辑:
  • 静态方法适用于那些不需要访问或修改类或实例状态的功能。

装饰器的基本使用

示例:简单的装饰器
def my_decorator(func):
    def wrapper():
        print("Something is happening before the function is called.")
        func()
        print("Something is happening after the function is called.")
    return wrapper

@my_decorator
def say_hello():
    print("Hello!")

# 调用函数
say_hello()
输出:
Something is happening before the function is called.
Hello!
Something is happening after the function is called.

在这个示例中:

  • my_decorator 是一个装饰器,它接收一个函数作为参数,并返回一个新的函数(wrapper)。
  • say_hello 函数被 @my_decorator 装饰,意味着调用 say_hello 时,实际上执行的是 wrapper 函数。

装饰器的叠加

你可以将多个装饰器应用到同一个函数或方法上。这时,装饰器从内向外依次应用。

示例:多个装饰器
def decorator1(func):
    def wrapper():
        print("Decorator 1")
        func()
    return wrapper

def decorator2(func):
    def wrapper():
        print("Decorator 2")
        func()
    return wrapper

@decorator1
@decorator2
def say_hello():
    print("Hello!")

# 调用函数
say_hello()
输出:
Decorator 1
Decorator 2
Hello!

在这个示例中,say_hello 函数首先被 decorator2 装饰,然后再被 decorator1 装饰。调用 say_hello 时,会先执行 decorator1 的代码,再执行 decorator2 的代码,最后执行原始函数。

@torch.inference_mode()

@torch.inference_mode() 是 PyTorch 提供的一个装饰器,用于将特定的代码块包裹在推理模式中。推理模式主要用于前向传播(inference)而不需要计算梯度,从而提高性能和节省内存。

功能和作用
禁用梯度计算:推理模式会禁用梯度计算,从而减少内存消耗和计算开销。与 torch.no_grad() 类似,但提供了一些额外的优化。
优化内存使用:在推理模式下,PyTorch 会进行一些内部优化,进一步减少内存使用。
只用于前向传播:适用于不需要反向传播的场景,如模型推理和评估。
使用方法
你可以将 @torch.inference_mode() 装饰器应用于函数,从而使得该函数在推理模式下执行。

import torch

# 创建一个简单的神经网络模型
class SimpleModel(torch.nn.Module):
    def __init__(self):
        super(SimpleModel, self).__init__()
        self.linear = torch.nn.Linear(10, 1)

    def forward(self, x):
        return self.linear(x)

model = SimpleModel()

# 创建输入张量
input_tensor = torch.randn(1, 10)

@torch.inference_mode()
def predict(model, input_tensor):
    return model(input_tensor)

# 调用推理函数
output = predict(model, input_tensor)
print(output)

在这个示例中,predict 函数被 @torch.inference_mode() 装饰,这意味着在执行该函数时,梯度计算被禁用,从而节省内存和计算资源。

与 torch.no_grad() 的区别
虽然 @torch.inference_mode() 和 torch.no_grad() 都用于禁用梯度计算,但 torch.inference_mode() 进行了一些额外的优化,使得它在推理时更高效。

示例:使用 torch.no_grad()

def predict_no_grad(model, input_tensor):
    with torch.no_grad():
        return model(input_tensor)

# 调用推理函数
output_no_grad = predict_no_grad(model, input_tensor)
print(output_no_grad)

两者的主要区别在于,torch.inference_mode() 作为装饰器可以直接应用于函数,使得代码更加简洁,而 torch.no_grad() 则需要使用 with 语句显式地包裹代码块。


网站公告

今日签到

点亮在社区的每一天
去签到