python常见注解汇总

以下是对上述各种类型提示注解的代码示例及说明:

**一、基本数据类型的类型提示**

def add_numbers(a: int, b: int) -> int:
    return a + b

说明:这个函数`add_numbers`接受两个参数`a`和`b`,类型提示明确表明这两个参数的类型都是整数。函数的返回值类型也被指定为整数。这样可以让使用者在调用这个函数时,清楚地知道应该传入什么类型的参数,以及期望得到什么类型的返回值。

问题:-> int是干嘛的?

在这段代码中,`int`的作用主要有以下两点:

作为参数类型标注

当用作参数类型标注时,即`a: int, b: int`,它明确表示函数`add_numbers`接收的两个参数`a`和`b`应该是整数类型。这为函数的使用者提供了清晰的参数类型要求,使得在调用这个函数时,他们知道应该传入整数类型的值。如果传入了其他不恰当的类型,比如字符串或浮点数,一些静态类型检查工具(如`mypy`)可能会发出警告,帮助开发者在开发过程中尽早发现类型不匹配的错误,提高代码的可靠性和可维护性。

作为返回值类型标注

在`-> int`中,`int`表示这个函数的返回值类型是整数。这同样为函数的使用者提供了明确的预期,让他们知道调用这个函数后会得到一个整数结果。这种明确的返回值类型标注有助于开发者更好地理解函数的行为和使用方式,避免在使用函数返回值时出现类型错误。

**二、容器类型的复杂类型提示**

def process_data(data: list[str], options: dict[str, bool]) -> tuple[int, str]:
    return 42, "result"

说明:函数`process_data`接受两个参数。参数`data`是一个字符串列表,意味着它应该是一个包含多个字符串元素的列表。参数`options`是一个字典,其中键是字符串类型,值是布尔类型。函数的返回值是一个包含整数和字符串的元组。通过类型提示,明确了函数输入和输出的具体类型结构,提高了代码的可读性和可维护性。

在这段代码中,-> tuple[int, str]:是函数的返回值类型标注。具体解释如下:

  • ->:这是一个用于指示函数返回值类型的符号。它出现在函数定义的参数列表之后,表示接下来要指定的是函数的返回值类型。

  • tuple[int, str]:这表示函数返回一个元组类型。元组中的元素类型分别为整数(int)和字符串(str)。

    • tuple是 Python 中的一种内置数据结构,用于存储多个不同类型的值。这里明确了这个函数将返回一个包含一个整数和一个字符串的元组。

整体来看,这个返回值类型标注告诉函数的使用者,当调用这个process_data函数时,可以期望得到一个包含一个整数和一个字符串的元组作为结果。这样的类型标注有助于提高代码的可读性和可维护性,让开发者更容易理解函数的行为和输出结果的类型结构。

**三、可调用类型的类型提示**

def apply_function(func: callable[[int], str], value: int) -> str:
    return func(value)

说明:这个函数接受两个参数。参数`func`是一个可调用对象,它接受一个整数参数并返回一个字符串。参数`value`是一个整数。函数的作用是将`value`传入可调用对象`func`并返回其结果,结果是一个字符串。类型提示明确了函数参数和返回值的类型关系,有助于在使用这个函数时确保传入正确类型的参数。

**四、可选类型的类型提示**

def process_value(value: int | None) -> str:
    if value is None:
        return "None value"
    else:
        return str(value)

说明:函数`process_value`接受一个参数`value`,这个参数可以是整数类型或者`None`。通过类型提示表明了这种可能性。在函数内部,根据参数的值是否为`None`进行不同的处理,分别返回不同的字符串结果。这样的类型提示可以让使用者清楚地知道这个参数可能的取值情况。

**五、联合类型的类型提示**

def handle_value(value: int | str) -> None:
    if isinstance(value, int):
        print(f"Integer value: {value}")
    else:
        print(f"String value: {value}")

说明:函数`handle_value`接受一个参数`value`,这个参数可以是整数类型或者字符串类型。类型提示明确了这种联合类型的可能性。在函数内部,通过判断参数的类型进行不同的处理,分别打印出整数或字符串的相关信息。这样的类型提示有助于提高代码的灵活性和可扩展性。

**二、装饰器注解**

除了前面提到的自定义装饰器,还有一些内置的装饰器也很常用:

1. `@lru_cache`:来自`functools`模块,用于缓存函数的结果,提高函数的执行效率。当使用相同的参数调用被装饰的函数时,直接返回缓存的结果,而不需要再次执行函数。   

- 例如:

from functools import lru_cache

   @lru_cache(maxsize=None)
   def fibonacci(n):
       if n <= 1:
           return n
       return fibonacci(n - 1) + fibonacci(n - 2)

 

2. `@property`、`@setter`、`@deleter`:用于将类的方法转换为属性的访问器、设置器和删除器。允许以更自然的方式访问和修改类的属性,同时可以在访问和修改过程中执行一些额外的逻辑。 

 - 例如:

  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.14 * self._radius * self._radius

以下是对这段代码的解释:

类的定义

class Circle:

这里定义了一个名为`Circle`的类,用于表示圆形。

构造方法

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

这是类的构造方法`__init__`,在创建`Circle`类的实例时会被调用。它接受一个参数`radius`,用于初始化圆形的半径,并将其存储在一个以单下划线开头的实例变量`_radius`中。以单下划线开头的变量在 Python 中通常表示一种约定,暗示这个变量是“受保护的”,虽然在实际使用中仍然可以直接访问,但不建议外部直接访问。

使用@property装饰器定义属性访问方法

1. 获取半径属性:

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

这里使用`@property`装饰器将`radius`方法转换为一个属性。这个属性可以像访问普通属性一样被访问,而不需要像调用方法一样加上括号。当访问`Circle`类的实例的`radius`属性时,会调用这个方法返回实例的半径值。

以下是用代码解释这段话的示例:

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

    @property
    def radius(self):
        print("Getting radius")
        return self._radius

c = Circle(5)

# 像访问普通属性一样访问 radius 属性,没有括号

print(c.radius) 

在这个例子中:

1. 定义了一个`Circle`类,在构造方法中初始化了一个私有属性`_radius`。
2. 使用`@property`装饰器将`radius`方法转换为属性。
3. 当通过`c.radius`来访问这个属性时,就如同访问一个普通的属性一样,不需要加括号调用方法。实际上会调用`radius`方法,这里打印了"Getting radius"以展示这个调用过程,然后返回实例的半径值。

如果没有使用`@property`装饰器,要获取半径值可能需要这样写:`print(c.radius())`,像调用方法一样,这就没有直接访问属性那么自然和简洁。

好了接着分析代码:

设置半径属性:

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

使用`@radius.setter`装饰器定义了半径属性的设置方法。当尝试给`Circle`类的实例的`radius`属性赋值时,会调用这个方法。在这个方法中,首先检查传入的值是否小于 0,如果是,则抛出一个`ValueError`异常。如果值合法,则将其赋值给实例变量`_radius`。

**四、计算面积的属性方法**

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

这里同样使用`@property`装饰器定义了一个名为`area`的属性。这个属性在访问时会自动计算并返回圆形的面积,面积的计算公式是`3.14 * 半径 * 半径`。

通过这种方式,使用属性装饰器使得对圆形的半径属性的访问和修改更加自然,同时也保证了半径值的合法性,并且方便地提供了计算圆形面积的功能,而不需要显式地调用方法来获取面积值。

在这段代码中,`@property`装饰器起到了以下几个重要作用:

1.将方法转换为属性访问** 1. 对于`radius`属性: - 通常情况下,如果没有`@property`装饰器,要获取或设置半径的值,可能需要通过调用方法来实现,比如`obj.get_radius()`和`obj.set_radius(new_value)`。但是使用了`@property`装饰器后,可以像访问普通属性一样直接使用`obj.radius`来获取半径值,使得代码更加简洁和直观。 - 这种方式符合面向对象编程中封装的原则,同时又提供了方便的属性访问方式,让使用者不需要了解底层实现是通过方法来获取和设置值的。

2. 对于`area`属性: - `area`属性通过`@property`装饰器变成了一个只读属性,不需要显式地调用一个方法来计算面积,而是直接通过`obj.area`就可以获取圆形的面积。这使得获取面积的操作更加自然和方便,就像访问一个普通的属性一样。

*实现属性的动态计算和控制访问**

1. 在`area`属性的计算中: - `area`属性的值是根据当前的半径值动态计算得到的。每次访问`area`属性时,都会重新计算面积,确保返回的是最新的面积值。这样可以避免在代码中重复计算面积的逻辑,提高了代码的效率和可维护性。 - 如果半径发生变化,再次访问`area`属性时会自动根据新的半径值计算面积,无需手动更新面积值。

2. 在`radius`属性的设置中: - 通过`@property`装饰器和对应的 setter 方法,可以在设置半径值时进行一些额外的逻辑控制。例如,在这段代码中,当设置半径值时,会检查新的值是否小于 0,如果是,则抛出一个异常,从而保证半径值的合法性。 - 这种方式可以在不破坏封装性的前提下,对属性的设置进行有效的控制和验证,提高了代码的健壮性。

 **三、上下文管理器注解**

1. `@contextmanager`:来自`contextlib`模块,用于将一个生成器函数转换为上下文管理器。上下文管理器可以在特定的代码块执行前后自动执行一些设置和清理操作。 

 - 例如:

 from contextlib import contextmanager

   @contextmanager
   def open_file(file_path, mode='r'):
       file = open(file_path, mode)
       try:
           yield file
       finally:
           file.close()

 以下是对这段代码的解释:

导入模块

from contextlib import contextmanager

从 Python 的`contextlib`模块中导入`contextmanager`装饰器。这个装饰器用于将一个生成器函数转换为上下文管理器。

定义函数并使用装饰器

@contextmanager
def open_file(file_path, mode='r'):
    file = open(file_path, mode)
    try:
        yield file
    finally:
        file.close()

1. `open_file`是一个被`@contextmanager`装饰的函数。这个函数接受两个参数:`file_path`表示要打开的文件路径,`mode`表示打开文件的模式,默认为只读模式('r')。

2. 在函数内部,首先使用`open`函数打开指定路径的文件,并将返回的文件对象赋值给变量`file`

3. 接着进入`try`块。在这个块中,使用`yield`关键字将打开的文件对象返回出去。这意味着当使用这个上下文管理器时,在`with`语句块中可以直接使用这个返回的文件对象进行操作

4. 如果在`with`语句块中发生了任何异常,`finally`块中的代码一定会被执行。在这个例子中,无论是否发生异常,都会确保文件被正确关闭。在`finally`块中,调用`file.close()`来关闭文件。

**三、使用上下文管理器**

以下是使用这个上下文管理器的示例:

with open_file('example.txt') as f:
    content = f.read()
    print(content)

在这个例子中,使用`with`语句调用`open_file`函数来打开文件`'example.txt'`。在`with`语句块中,可以像使用普通打开的文件对象一样使用变量`f`进行读取操作。当`with`语句块执行完毕,无论是否发生异常,上下文管理器都会确保文件被正确关闭。

在 Python 中,`@contextmanager`装饰器的作用主要是将一个生成器函数转换为上下文管理器

具体来说:

简化资源管理

当你处理需要打开和关闭的资源时,如文件、数据库连接、网络连接等,通常需要确保在使用完资源后正确地关闭它们,以防止资源泄漏。使用`@contextmanager`可以简化这个过程。

例如在你给出的代码中,用于打开文件。在使用文件时,不用再显式地调用`open`和`close`方法,而是通过上下文管理器自动处理文件的打开和关闭。

清晰的代码结构

使用上下文管理器可以使代码结构更加清晰和易读。通过`with`语句块,可以明确地标识出资源的使用范围,并且确保资源在离开这个范围时被正确释放。

例如:

with open_file('example.txt') as f:
    content = f.read()
    print(content)

这里很清晰地表明在`with`语句块内使用文件对象`f`,一旦退出这个块,文件就会自动关闭。

异常处理

上下文管理器会自动处理在`with`语句块中发生的异常。如果在`with`语句块中发生了异常,`finally`块中的代码(在生成器函数中)仍然会被执行,确保资源被正确清理。

例如在`open_file`函数中,无论在`with`语句块中发生什么情况,文件都会在`finally`块中被关闭。

**四、抽象基类注解**

1. `@abstractmethod`:来自`abc`模块,用于标记抽象基类中的抽象方法。子类必须实现抽象基类中的所有抽象方法,否则也会被视为抽象类。 

 - 例如:

import abc

   class Shape(metaclass=abc.ABCMeta):
       @abstractmethod
       def area(self):
           pass

       @abstractmethod
       def perimeter(self):
           pass

以下是对这段代码的解释:

导入抽象基类模块

import abc

这行代码导入了 Python 的抽象基类模块`abc`(Abstract Base Classes)。这个模块提供了创建抽象基类的工具,用于定义一组必须在子类中实现的抽象方法。

**二、定义抽象基类**

class Shape(metaclass=abc.ABCMeta):

这里定义了一个名为`Shape`的类,并将其元类设置为`abc.ABCMeta`。这使得`Shape`成为一个抽象基类,不能被实例化。

**三、定义抽象方法**

 @abstractmethod
   def area(self):
       pass

   这里使用`@abstractmethod`装饰器定义了一个抽象方法`area`。这意味着任何继承自`Shape`的子类都必须实现这个方法。如果子类没有实现这个方法,那么尝试实例化子类时会引发错误。

 @abstractmethod
   def perimeter(self):
       pass

   同样,这里定义了另一个抽象方法`perimeter`。子类也必须实现这个方法。

在 Python 中,`@abstractmethod`装饰器的作用主要有以下几点:

定义抽象方法

1. 强制子类实现:当一个方法被`@abstractmethod`装饰后,它就成为了抽象方法。这意味着任何继承自包含抽象方法的父类的子类都必须实现这个抽象方法。如果子类没有实现所有的抽象方法,那么尝试实例化子类时会引发错误。

   例如在你给出的代码中,`Shape`类中的`area`和`perimeter`方法被标记为抽象方法,任何继承自`Shape`的子类都必须提供这两个方法的具体实现。

2. 明确接口规范:通过定义抽象方法,为一组相关的类明确了一个共同的接口规范。这使得开发者可以在更高层次上设计代码,只关心类应该具有的行为,而不必考虑具体的实现细节。

实现多态性和代码复用

1. 多态性:在面向对象编程中,多态性允许不同的对象对同一消息做出不同的响应。通过抽象基类和抽象方法,可以定义一组通用的操作,不同的子类可以根据自己的具体情况实现这些操作,从而实现多态性。

   例如,对于不同形状的类(如圆形、矩形等),它们都可以继承自`Shape`抽象基类,并实现`area`和`perimeter`方法。在使用这些形状类的代码中,可以通过调用抽象基类中定义的方法来实现多态行为,而不必关心具体的形状类型。

2. 代码复用:抽象基类可以提供一些通用的功能和代码,子类可以继承这些功能并根据需要进行扩展。抽象方法则定义了子类必须实现的接口,确保了代码的一致性和可维护性。

   例如,`Shape`抽象基类可以包含一些通用的方法或属性,如用于打印形状信息的方法。子类可以继承这些通用功能,并在实现抽象方法时添加自己特定的逻辑。

**四、作用总结**

使用抽象基类和抽象方法可以为一组相关的类定义一个共同的接口。在这个例子中,`Shape`抽象基类定义了所有形状应该具有的基本行为,即计算面积和周长。这样可以确保任何表示形状的类都实现了这些必要的方法,提高了代码的一致性和可维护性。同时,它也为开发者提供了一种清晰的方式来设计和组织面向对象的代码结构。

 

**五、数据类注解**

1. `@dataclass`:来自`dataclasses`模块,用于自动生成一些常见的特殊方法,如`__init__`、`__repr__`、`__eq__`等,方便创建数据类。 

 - 例如:

 from dataclasses import dataclass

   @dataclass
   class Point:
       x: int
       y: int

以下是对这段代码的解释:

**一、导入模块**

from dataclasses import dataclass

这行代码从 Python 的`dataclasses`模块中导入了`dataclass`装饰器。这个模块在 Python 3.7 及以上版本中可用,它提供了一种方便的方式来创建数据类。

**二、定义数据类**

@dataclass
class Point:
    x: int
    y: int

1. `@dataclass`装饰器应用于`Point`类,这使得`Point`类成为一个数据类。数据类是一种专门用于存储数据的类,它自动生成一些常见的特殊方法,如`__init__`、`__repr__`、`__eq__`等,使得定义简单的数据结构更加方便。

2. 在类定义中,直接声明了属性`x`和`y`,并指定了它们的类型为`int`。这使得创建`Point`对象时可以直接通过参数初始化`x`和`y`属性。

**三、作用总结**

1. 简化类定义:使用`@dataclass`装饰器可以避免手动编写大量的样板代码,如构造函数、`__repr__`方法等。这使得代码更加简洁、易读,并且减少了出错的可能性。

2. 自动生成方法:数据类自动生成的方法可以方便地进行对象的初始化、打印和比较等操作。例如,`__repr__`方法会生成一个易于阅读的对象表示形式,方便调试和日志记录。`__eq__`方法可以方便地比较两个对象是否相等。

3. 提高开发效率:在处理数据结构时,使用数据类可以快速定义和使用简单的数据对象,提高开发效率。特别是在处理大量数据或需要频繁创建和操作数据对象的情况下,数据类可以大大简化代码

  

  • 0
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值