" />

Python Type Hint类型注解

2024/5/8 13:40:22

原文地址:https://realpython.com/python-type-checking/
在本指南中,你将了解Python类型检查。传统上,Python解释器以灵活但隐式的方式处理类型。Python的最新版本允许你指定可由不同工具使用的显式类型提示,以帮助您更有效地开发代码。

通过本教程,你将学到以下内容:

类型注解和提示(Type annotations and type hints)

代码里添加静态类型

静态类型检查

运行时强制类型一致

这是一个全面的指南,将涵盖很多领域。如果您只是想快速了解一下类型提示在Python中是如何工作的,并查看类型检查是否包括在您的代码中,那么您不需要阅读全部内容。Hello Types和正反两部分将让您大致了解类型检查是如何工作的,并介绍它在什么时候有用。

Type Systems

所有的编程语言都包括某种类型的系统,该系统将它可以处理的对象类别以及如何处理这些类别形式化。例如,类型系统可以定义一个数字类型,其中42是数字类型对象的一个例子。

动态类型

Python是一种动态类型语言。这意味着Python解释器仅在代码运行时进行类型检查,并且允许变量的类型在其生命周期内进行更改。以下示例演示了Python具有动态类型:

if False:

... 1 + "two" # This line never runs, so no TypeError is raised

... else:

... 1 + 2

...

3

1 + "two" # Now this is type checked, and a TypeError is raised

TypeError: unsupported operand type(s) for +: 'int' and 'str'

在上面例子中,if从未运行过,因此它未被类型检查过。成功运行了else部分得到结果3,紧接着下面计算1 +“2”时,因为类型不一致所以,产生一个类型错误。

看下一个例子,如果改变一个变量的值的类型

thing = "Hello"

type(thing)

<class 'str'>

thing = 28.1

type(thing)

<class 'float'>

type()返回对象的类型。这些示例确认允许更改事物的类型,并且Python在更改时正确地推断出类型。

静态类型

与动态类型相反的是静态类型。在不运行程序的情况下执行静态类型检查。在大多数静态类型语言中,编译是在程序时完成的。例如C和Java,

对于静态类型,通常不允许变量改变类型,尽管可能存在将变量转换为不同类型的机制。

让我们看一个静态类型语言的快速示例。请考虑以下Java代码段:

String thing;

thing = "Hello";

第一行声明thing的类型是String,所以后面的赋值也必须指定字符串类型,如果你给thing=2就会出错,但是python就不会出错。

虽然,Python始终是一种动态类型语言。但是,PEP 484引入了类型提示,这使得还可以对Python代码进行静态类型检查。

与大多数其他静态类型语言中的工作方式不同,类型提示本身不会导致Python强制执行类型。顾名思义,键入提示只是建议类型。

鸭子类型

在谈论Python时经常使用的另一个术语是鸭子打字。这个绰号来自短语“如果它像鸭子一样行走,它像鸭子一样嘎嘎叫,那它一定是鸭子”(或其任何变化)。

鸭子类型是一个与动态类型相关的概念,其中对象的类型或类不如它定义的方法重要。使用鸭子类型根本不需要检查类型,而是检查给定方法或属性是否存在。

下面一个例子, 你可在python所有的对象中使用 len() 的魔法函数len() 方法:

class TheHobbit:

... def len(self):

... return 95022

...

the_hobbit = TheHobbit()

len(the_hobbit)

95022

实际len()方法就是下面的这种方法实现的:

def len(obj):

return obj.__len__()

由此发现,对象也可以像str、list

、dict那样使用len方法,只不过需要重新写len魔法函数即可。

Hello Types

在本节中,您将看到如何向函数添加类型提示。下面的函数通过添加适当的大写字母和装饰线将文本字符串转换为标题:

def headline(text, align=True):

if align:return f"{text.title()}\n{'-' * len(text)}"else:return f" {text.title()} ".center(50, "o")

默认情况下,函数返回与下划线对齐的左侧标题。通过将align标志设置为False,您还可以选择使用o围绕字符串:

print(headline("python type checking"))

Python Type Checking


print(headline("python type checking", align=False))

oooooooooooooo Python Type Checking oooooooooooooo

是时候给我们第一个类型加个提示了!要向函数中添加关于类型的信息,只需如下注释其参数和返回值:

def headline(text: str, align: bool = True) -> str:

...

text: str 意思是text值类型是str, 类似的, 可选参数 align 指定其类型为bool并给定默认值True. 最后, -> str 表示函数headline() 返回值类型为str。

在代码风格方面,PEP 8建议如下::

对冒号使用常规规则,即冒号前没有空格,冒号后面有一个空格:text: str。

将参数注释与默认值组合时,在=符号周围使用空格:align: bool = True。

def headline(...) - > str,使用空格围绕。

print(headline("python type checking", align="left"))

Python Type Checking


但是如果传入的参数类型不是指定的参数类型,程序不会出现错误,此时可以使用类型检查模块通过提示内容确定是否类型输入正确,如mypy。

你可以通过 pip安装:

$ pip install mypy

将以下代码放在名为headlines.py的文件中:

headlines.py

def headline(text: str, align: bool = True) -> str:

if align:return f"{text.title()}\n{'-' * len(text)}"else:return f" {text.title()} ".center(50, "o")

print(headline("python type checking"))

print(headline("use mypy", align="center"))

然后通过mypy运行上面的文件:

$ mypy headlines.py

headlines.py:10: error: Argument "align" to "headline" has incompatible

                    type "str"; expected "bool"

根据类型提示,Mypy能够告诉我们我们在第10行使用了错误的类型

这样说明一个问题参数名align不是很好确定参数是bool类型,我们将代码改成下面这样,换一个识别度高的参数名centered。

headlines.py

def headline(text: str, centered: bool = False):

  if not centered:return f"{text.title()}\n{'-' * len(text)}"else:return f" {text.title()} ".center(50, "o")

print(headline("python type checking"))

print(headline("use mypy", centered=True))

再次运行文件发现没有错误提示,ok。

$ mypy headlines.py

$

然后就可以打印结果了

$ python headlines.py

Python Type Checking


oooooooooooooooooooo Use Mypy oooooooooooooooooooo

第一个标题与左侧对齐,而第二个标题居中。

Pros and Cons

类型提示的增加方便了IDE的代码提示功能,我们看到下面text使用.即可得到str使用的一些方法和熟悉。

类型提示可帮助您构建和维护更清晰的体系结构。编写类型提示的行为迫使您考虑程序中的类型。虽然Python的动态特性是其重要资产之一,但是有意识地依赖于鸭子类型,重载方法或多种返回类型是一件好事。

需要注意的是,类型提示会在启动时带来轻微的损失。如果您需要使用类型模块,那么导入时间可能很长,尤其是在简短的脚本中。

那么,您应该在自己的代码中使用静态类型检查吗?这不是一个全有或全无的问题。幸运的是,Python支持渐进式输入的概念。这意味着您可以逐渐在代码中引入类型。没有类型提示的代码将被静态类型检查器忽略。因此,您可以开始向关键组件添加类型,只要它能为您增加价值,就可以继续。

关于是否向项目添加类型的一些经验法则:

如果您刚开始学习Python,可以安全地等待类型提示,直到您有更多经验。

类型提示在短暂抛出脚本中增加的价值很小。

在其他人使用的库中,尤其是在PyPI上发布的库中,类型提示会增加很多价值。使用库的其他代码需要这些类型提示才能正确地进行类型检查。

在较大的项目中,类型提示可以帮助您理解类型是如何在代码中流动的,强烈建议您这样做。在与他人合作的项目中更是如此。

Bernat Gabor在他的文章《Python中类型提示的状态》中建议,只要值得编写单元测试,就应该使用类型提示。实际上,类型提示在代码中扮演着类似于测试的角色:它们帮助开发人员编写更好的代码。

Annotations[注解]

Python 3.0中引入了注释,最初没有任何特定用途。它们只是将任意表达式与函数参数和返回值相关联的一种方法。

多年以后,PEP 484根据Jukka Lehtosalo博士项目Mypy所做的工作,定义了如何向Python代码添加类型提示。添加类型提示的主要方法是使用注释。随着类型检查变得越来越普遍,这也意味着注释应该主要保留给类型提示。

接下来的章节将解释注释如何在类型提示的上下文中工作。

Function Annotations[函数注解]

之前我们也提到过函数的注解例子向下面这样:

def func(arg: arg_type, optarg: arg_type = default) -> return_type:

...

对于参数,语法是参数:注释,而返回类型使用->注释进行注释。请注意,注释必须是有效的Python表达式。

以下简单示例向计算圆周长的函数添加注释:

import math

def circumference(radius: float) -> float:

return 2 * math.pi * radius

通调用circumference对象的annotations魔法函数可以输出函数的注解信息。

circumference(1.23)

7.728317927830891

circumference.annotations

{'radius': <class 'float'>, 'return': <class 'float'>}

有时您可能会对Mypy如何解释您的类型提示感到困惑。对于这些情况,有一些特殊的Mypy表达式:reveal type()和reveal local()。您可以在运行Mypy之前将这些添加到您的代码中,Mypy将报告它所推断的类型。例如,将以下代码保存为reveal.py。

reveal.py

import math

reveal_type(math.pi)

radius = 1

circumference = 2 math.pi radius

reveal_locals()

然后通过mypy运行上面代码

$ mypy reveal.py

reveal.py:4: error: Revealed type is 'builtins.float'

reveal.py:8: error: Revealed local types are:

reveal.py:8: error: circumference: builtins.float

reveal.py:8: error: radius: builtins.int

即使没有任何注释,Mypy也正确地推断了内置数学的类型。以及我们的局部变量半径和周长。

注意:以上代码需要通过mypy运行,如果用python运行会报错,另外mypy 版本不低于 0.610

Variable Annotations[变量注解]

有时类型检查器也需要帮助来确定变量的类型。变量注释在PEP 526中定义,并在Python 3.6中引入。语法与函数参数注释相同:

pi: float = 3.142

def circumference(radius: float) -> float:

return 2 * pi * radius

pi被声明为float类型。

注意: 静态类型检查器能够很好地确定3.142是一个浮点数,因此在本例中不需要pi的注释。随着您对Python类型系统的了解越来越多,您将看到更多有关变量注释的示例。.

变量注释存储在模块级annotations字典中::

circumference(1)

6.284

annotations

{'pi': <class 'float'>}

即使只是定义变量没有给赋值,也可以通过annotations获取其类型。虽然在python中没有赋值的变量直接输出是错误的。

nothing: str

nothing

NameError: name 'nothing' is not defined

annotations

{'nothing': <class 'str'>}

Type Comments[类型注解]

如上所述,注释是在Python 3中引入的,并且它们没有被反向移植到Python 2.这意味着如果您正在编写需要支持旧版Python的代码,则无法使用注释。

要向函数添加类型注释,您可以执行以下操作:

import math

def circumference(radius):

# type: (float) -> floatreturn 2 * math.pi * radius

类型注释只是注释,所以它们可以用在任何版本的Python中。

类型注释由类型检查器直接处理,所以不存在annotations字典对象中:

circumference.annotations

{}

类型注释必须以type: 字面量开头,并与函数定义位于同一行或下一行。如果您想用几个参数来注释一个函数,您可以用逗号分隔每个类型:

def headline(text, width=80, fill_char="-"):

# type: (str, int, str) -> strreturn f" {text.title()} ".center(width, fill_char)

print(headline("type comments work", width=40))

您还可以使用自己的注释在单独的行上编写每个参数:

headlines.py

def headline(

  text,          # type: strwidth=80,      # type: intfill_char="-",  # type: str

): # type: (...) -> str

  return f" {text.title()} ".center(width, fill_char)

print(headline("type comments work", width=40))

通过Python和Mypy运行示例:

$ python headlines.py

---------- Type Comments Work ----------

$ mypy headline.py

$

如果传入一个字符串width="full",再次运行mypy会出现一下错误。

$ mypy headline.py

headline.py:10: error: Argument "width" to "headline" has incompatible

                  type "str"; expected "int"

您还可以向变量添加类型注释。这与您向参数添加类型注释的方式类似:

pi = 3.142 # type: float

上面的例子可以检测出pi是float类型。

So, Type Annotations or Type Comments?

所以向自己的代码添加类型提示时,应该使用注释还是类型注释?简而言之:尽可能使用注释,必要时使用类型注释。

注释提供了更清晰的语法,使类型信息更接近您的代码。它们也是官方推荐的写入类型提示的方式,并将在未来进一步开发和适当维护。

类型注释更详细,可能与代码中的其他类型注释冲突,如linter指令。但是,它们可以用在不支持注释的代码库中。

还有一个隐藏选项3:存根文件。稍后,当我们讨论向第三方库添加类型时,您将了解这些。

存根文件可以在任何版本的Python中使用,代价是必须维护第二组文件。通常,如果无法更改原始源代码,则只需使用存根文件。

玩转Type Hint, Part 1

到目前为止,您只在类型提示中使用了str,float和bool等基本类型。但是Python类型系统非常强大,它可以支持多种更复杂的类型。

在本节中,您将了解有关此类型系统的更多信息,同时实现简单的纸牌游戏。您将看到如何指定:

序列和映射的类型,如元组,列表和字典

键入别名,使代码更容易阅读

该函数和方法不返回任何内容

可以是任何类型的对象

在简要介绍了一些类型理论之后,您将看到更多用Python指定类型的方法。您可以在这里找到代码示例:

https://github.com/realpython/materials/tree/master/python-type-checking

Example: A Deck of Cards

以下示例显示了一副常规纸牌的实现:

game.py

import random

SUITS = " ".split()

RANKS = "2 3 4 5 6 7 8 9 10 J Q K A".split()

def create_deck(shuffle=False):

  """Create a new deck of 52 cards"""deck = [(s, r) for r in RANKS for s in SUITS]if shuffle:random.shuffle(deck)return deck

def deal_hands(deck):

"""Deal the cards in the deck into four hands"""return (deck[0::4], deck[1::4], deck[2::4], deck[3::4])

def play():

"""Play a 4-player card game"""deck = create_deck(shuffle=True)names = "P1 P2 P3 P4".split()hands = {n: h for n, h in zip(names, deal_hands(deck))}for name, cards in hands.items():card_str = " ".join(f"{s}{r}" for (s, r) in cards)print(f"{name}: {card_str}")

if name == "main":

play()

每张卡片都表示为套装和等级的字符串元组。卡组表示为卡片列表。create_deck()创建一个由52张扑克牌组成的常规套牌,并可选择随机播放这些牌。deal_hands()将牌组交给四名玩家。

最后,play()扮演游戏。截至目前,它只是通过构建一个洗牌套牌并向每个玩家发牌来准备纸牌游戏。以下是典型输出:

$ python game.py

P4: 9 9 2 7 7 A 6 K 5 6 3 3 Q

P1: A 2 10 J 10 4 5 Q 5 6 A 5 4

P2: 2 7 8 K 3 3 K J A 7 6 10 K

P3: 2 8 8 J Q 9 J 4 8 10 9 4 Q

下面让我一步一步对上面的代码进行拓展。

Sequences and Mappings[序列和映射]

让我们为我们的纸牌游戏添加类型提示。换句话说,让我们注释函数create_deck(),deal_hands()和play()。第一个挑战是你需要注释复合类型,例如用于表示卡片组的列表和用于表示卡片本身的元组。

对于像str、float和bool这样的简单类型,添加类型提示就像使用类型本身一样简单:

name: str = "Guido"

pi: float = 3.142

centered: bool = False

对于复合类型,可以执行相同的操作:

names: list = ["Guido", "Jukka", "Ivan"]

version: tuple = (3, 7, 1)

options: dict = {"centered": False, "capitalize": True}

上面的注释还是不完善,比如names我们只是知道这是list类型,但是我们不知道list里面的元素数据类型

typing模块为我们提供了更精准的定义:

from typing import Dict, List, Tuple

names: List[str] = ["Guido", "Jukka", "Ivan"]

version: Tuple[int, int, int] = (3, 7, 1)

options: Dict[str, bool] = {"centered": False, "capitalize": True}

需要注意的是,这些类型中的每一个都以大写字母开头,并且它们都使用方括号来定义项的类型:

names 是一个str类型的list数组。

version 是一个含有3个int类型的元组

options 是一个字典键名类型str,简直类型bool

Typing 还包括其他的很多类型比如 Counter, Deque, FrozenSet, NamedTuple, 和 Set.此外,该模块还包括其他的类型,你将在后面的部分中看到.

让我们回到扑克游戏. 因为卡片是有2个str组成的元组定义的. 所以你可以写作Tuple[str, str],所以函数create_deck()返回值的类型就是 List[Tuple[str, str]].

def create_deck(shuffle: bool = False) -> List[Tuple[str, str]]:

"""Create a new deck of 52 cards"""deck = [(s, r) for r in RANKS for s in SUITS]if shuffle:random.shuffle(deck)return deck

除了返回值之外,您还将bool类型添加到可选的shuffle参数中。

注意: 元组和列表的声明是有区别的

元组是不可变序列,通常由固定数量的可能不同类型的元素组成。例如,我们将卡片表示为套装和等级的元组。通常,您为n元组编写Tuple[t_1,t_2,...,t_n]。

列表是可变序列,通常由未知数量的相同类型的元素组成,例如卡片列表。无论列表中有多少元素,注释中只有一种类型:List[t]。

在许多情况下,你的函数会期望某种序列,并不关心它是列表还是元组。在这些情况下,您应该使用typing.Sequence在注释函数参数时:

from typing import List, Sequence

def square(elems: Sequence[float]) -> List[float]:

return [x**2 for x in elems]

使用 Sequence 是一个典型的鸭子类型的例子. 也就意味着可以使用len() 和 .

getitem()等方法。

Type Aliases[类型别名]

使用嵌套类型(如卡片组)时,类型提示可能会变得非常麻烦。你可能需要仔细看List [Tuple [str,str]],才能确定它与我们的一副牌是否相符.

现在考虑如何注释deal_hands()函数:

def deal_hands(deck: List[Tuple[str, str]]) -> Tuple[

List[Tuple[str, str]],List[Tuple[str, str]],List[Tuple[str, str]],List[Tuple[str, str]],

]:

"""Deal the cards in the deck into four hands"""return (deck[0::4], deck[1::4], deck[2::4], deck[3::4])

这也太麻烦了!

不怕,我们还可以使用起别名的方式把注解的类型赋值给一个新的变量,方便在后面使用,就像下面这样:

from typing import List, Tuple

Card = Tuple[str, str]

Deck = List[Card]

现在我们就可以使用别名对之前的代码进行注解了:

def deal_hands(deck: Deck) -> Tuple[Deck, Deck, Deck, Deck]:

"""Deal the cards in the deck into four hands"""return (deck[0::4], deck[1::4], deck[2::4], deck[3::4])

类型别名让我们的代码变的简洁了不少,我们可以打印变量看里面具体的值:

from typing import List, Tuple

Card = Tuple[str, str]

Deck = List[Card]

Deck

typing.List[typing.Tuple[str, str]]

当输出Deck的时候可以看到其最终的类型.

函数无返回值时

对于没有返回值的函数,我们可以指定None:

play.py

def play(player_name: str) -> None:

  print(f"{player_name} plays")

ret_val = play("Filip")

通过mypy检测上面代码

$ mypy play.py

play.py:6: error: "play" does not return a value

作为一个更奇特的情况,请注意您还可以注释从未期望正常返回的函数。这是使用NoReturn完成的:

from typing import NoReturn

def black_hole() -> NoReturn:

raise Exception("There is no going back ...")

因为black_hole()总是引发异常,所以它永远不会正确返回。

Example: Play Some Cards

让我们回到我们的纸牌游戏示例。在游戏的第二个版本中,我们像以前一样向每个玩家发放一张牌。然后选择一个开始玩家并且玩家轮流玩他们的牌。虽然游戏中没有任何规则,所以玩家只会玩随机牌:

game.py

import random

from typing import List, Tuple

SUITS = " ".split()

RANKS = "2 3 4 5 6 7 8 9 10 J Q K A".split()

Card = Tuple[str, str]

Deck = List[Card]

def create_deck(shuffle: bool = False) -> Deck:

"""Create a new deck of 52 cards"""deck = [(s, r) for r in RANKS for s in SUITS]if shuffle:random.shuffle(deck)return deck

def deal_hands(deck: Deck) -> Tuple[Deck, Deck, Deck, Deck]:

"""Deal the cards in the deck into four hands"""return (deck[0::4], deck[1::4], deck[2::4], deck[3::4])

def choose(items):

"""Choose and return a random item"""return random.choice(items)

def player_order(names, start=None):

"""Rotate player order so that start goes first"""if start is None:start = choose(names)start_idx = names.index(start)return names[start_idx:] + names[:start_idx]

def play() -> None:

"""Play a 4-player card game"""deck = create_deck(shuffle=True)names = "P1 P2 P3 P4".split()hands = {n: h for n, h in zip(names, deal_hands(deck))}start_player = choose(names)turn_order = player_order(names, start=start_player)# Randomly play cards from each player's hand until emptywhile hands[start_player]:for name in turn_order:card = choose(hands[name])hands[name].remove(card)print(f"{name}: {card[0] + card[1]:<3}  ", end="")print()

if name == "main":

play()

请注意,除了更改play()之外,我们还添加了两个需要类型提示的新函数:choose()和player_order()。在讨论我们如何向它们添加类型提示之前,以下是运行游戏的示例输出:

$ python game.py

P3: 10 P4: 4 P1: 8 P2: Q

P3: 8 P4: 6 P1: 5 P2: K

P3: 9 P4: J P1: A P2: A

P3: Q P4: 3 P1: 7 P2: A

P3: 4 P4: 6 P1: 2 P2: K

P3: K P4: 7 P1: 7 P2: 2

P3: 10 P4: 4 P1: 5 P2: 3

P3: Q P4: K P1: J P2: 9

P3: 2 P4: 4 P1: 9 P2: 10

P3: A P4: 5 P1: J P2: Q

P3: 8 P4: 7 P1: 3 P2: J

P3: 3 P4: 10 P1: 9 P2: 2

P3: 6 P4: 6 P1: 5 P2: 8

在该示例中,随机选择玩家P3作为起始玩家。反过来,每个玩家都会玩一张牌:先是P3,然后是P4,然后是P1,最后是P2。只要手中有任何左手,玩家就会持续打牌。

The Any Type

choose()适用于名称列表和卡片列表(以及任何其他序列)。为此添加类型提示的一种方法是:

import random

from typing import Any, Sequence

def choose(items: Sequence[Any]) -> Any:

return random.choice(items)

这或多或少意味着它:items是一个可以包含任何类型的项目的序列,而choose()将返回任何类型的这样的项目。不是很严谨,此时请考虑以下示例:

choose.py

import random

from typing import Any, Sequence

def choose(items: Sequence[Any]) -> Any:

  return random.choice(items)

names = ["Guido", "Jukka", "Ivan"]

reveal_type(names)

name = choose(names)

reveal_type(name)

虽然Mypy会正确推断名称是字符串列表,但由于使用了任意类型,在调用choose ( )后,该信息会丢失:

$ mypy choose.py

choose.py:10: error: Revealed type is 'builtins.list[builtins.str*]'

choose.py:13: error: Revealed type is 'Any'

由此可以得知,如果使用了Any使用mypy的时候将不容易检测。

玩转 Type Hint, Part 2

import random

from typing import Any, Sequence

def choose(items: Sequence[Any]) -> Any:

return random.choice(items)

使用Any的问题在于您不必要地丢失类型信息。您知道如果将一个字符串列表传递给choose(),它将返回一个字符串。

Type Variables[类型声明]

类型声明是一个特殊变量声明,可以采用任何类型,具体取决于具体情况。

让我们创建一个有效封装choose()行为的类型变量:

choose.py

import random

from typing import Sequence, TypeVar

Choosable = TypeVar("Chooseable")

def choose(items: Sequence[Choosable]) -> Choosable:

  return random.choice(items)

names = ["Guido", "Jukka", "Ivan"]

reveal_type(names)

name = choose(names)

reveal_type(name)

类型声明必须使用类型模块中的 TypeVar 定义。使用时,类型声明的范围覆盖所有可能的类型,并获取最特定的类型。在这个例子中,name现在是一个str

$ mypy choose.py

choose.py:12: error: Revealed type is 'builtins.list[builtins.str*]'

choose.py:15: error: Revealed type is 'builtins.str*'

考虑一些其他例子:

choose_examples.py

from choose import choose

reveal_type(choose(["Guido", "Jukka", "Ivan"]))

reveal_type(choose([1, 2, 3]))

reveal_type(choose([True, 42, 3.14]))

reveal_type(choose(["Python", 3, 7])

前两个例子应该有类型str和int,但是后两个呢?单个列表项有不同的类型,在这种情况下,可选择类型变量会尽最大努力适应:

$ mypy choose_examples.py

choose_examples.py:5: error: Revealed type is 'builtins.str*'

choose_examples.py:6: error: Revealed type is 'builtins.int*'

choose_examples.py:7: error: Revealed type is 'builtins.float*'

choose_examples.py:8: error: Revealed type is 'builtins.object*'

正如您已经看到的那样bool是int的子类型,它也是float的子类型。所以在第三个例子中,choose()的返回值保证可以被认为是浮点数。在最后一个例子中,str和int之间没有子类型关系,因此关于返回值可以说最好的是它是一个对象。

请注意,这些示例都没有引发类型错误。有没有办法告诉类型检查器,选择( )应该同时接受字符串和数字,但不能同时接受两者?

您可以通过列出可接受的类型来约束类型变量:

choose.py

import random

from typing import Sequence, TypeVar

Choosable = TypeVar("Choosable", str, float)

def choose(items: Sequence[Choosable]) -> Choosable:

  return random.choice(items)

reveal_type(choose(["Guido", "Jukka", "Ivan"]))

reveal_type(choose([1, 2, 3]))

reveal_type(choose([True, 42, 3.14]))

reveal_type(choose(["Python", 3, 7]))

现在Choosable只能是str或float,而Mypy会注意到最后一个例子是一个错误:

$ mypy choose.py

choose.py:11: error: Revealed type is 'builtins.str*'

choose.py:12: error: Revealed type is 'builtins.float*'

choose.py:13: error: Revealed type is 'builtins.float*'

choose.py:14: error: Revealed type is 'builtins.object*'

choose.py:14: error: Value of type variable "Choosable" of "choose"

                cannot be "object"

还要注意,在第二个例子中,即使输入列表只包含int对象,该类型也被认为是float类型的。这是因为Choosable仅限于str和float,int是float的一个子类型。

在我们的纸牌游戏中,我们想限制choose()只能用str和Card类型:

Choosable = TypeVar("Choosable", str, Card)

def choose(items: Sequence[Choosable]) -> Choosable:

...

我们简要地提到Sequence表示列表和元组。正如我们所指出的,一个Sequence可以被认为是一个duck类型,因为它可以是实现了. len ()和. getitem ()的任何对象。

鸭子类型和协议

回想一下引言中的以下例子:

def len(obj):

return obj.__len__()

len()方法可以返回任何实现len魔法函数的对象的长度,那我们如何在len()里添加类型提示,尤其是参数obj的类型表示呢?

答案隐藏在学术术语structural subtyping[https://en.wikipedia.org/wiki/Structural_type_system]。structural subtyping的一种方法是根据它们是normal的还是structural的:

在normal系统中,类型之间的比较基于名称和声明。Python类型系统大多是名义上的,因为它们的子类型关系,可以用int来代替float。

在structural系统中,类型之间的比较基于结构。您可以定义一个结构类型“大小”,它包括定义的所有实例。len(),无论其标称类型如何.

目前正在通过PEP 544为Python带来一个成熟的结构类型系统,该系统旨在添加一个称为协议的概念。尽管大多数PEP 544已经在Mypy中实现了。

协议指定了一个或多个实现的方法。例如,所有类定义。len()完成typing.Sized协议。因此,我们可以将len()注释如下:

from typing import Sized

def len(obj: Sized) -> int:

return obj.__len__()

除此之外,在Typing中还包括以下模块 Container, Iterable, Awaitable, 还有 ContextManager.

你也可以声明自定的协议,通过导入typing_extensions模块中的Protocol协议对象,然后写一个继承该方法的子类,像下面这样:

from typing_extensions import Protocol

class Sized(Protocol):

def __len__(self) -> int: ...

def len(obj: Sized) -> int:

return obj.__len__()

需要通过pip安装上面使用的第三方模块

pip install typing-extensions.

Optional 类型

在python中有一种公共模式,就是设置参数的默认值None,这样做通常是为了避免可变默认值的问题,或者让一个标记值标记特殊行为。

在上面的card 例子中, 函数 player_order() 使用 None 作为参数start的默认值,表示还没有指定玩家:

def player_order(names, start=None):

"""Rotate player order so that start goes first"""if start is None:start = choose(names)start_idx = names.index(start)return names[start_idx:] + names[:start_idx]

这给类型提示带来的挑战是,通常start应该是一个字符串。但是,它也可能采用特殊的非字符串值“None”。

为解决上面的问题,这里可以使用Optional类型:

from typing import Sequence, Optional

def player_order(

names: Sequence[str], start: Optional[str] = None

) -> Sequence[str]:

...

等价于Union类型的 Union[None, str],意思是这个参数的值类型为str,默认的话可以是

请注意,使用Optional或Union时,必须注意变量是否在后面有操作。比如上面的例子通过判断start是否为None。如果不判断None的情况,在做静态类型检查的时候会发生错误:

1 # player_order.py

2

3 from typing import Sequence, Optional

4

5 def player_order(

6 names: Sequence[str], start: Optional[str] = None

7 ) -> Sequence[str]:

8 start_idx = names.index(start)

9 return names[start_idx:] + names[:start_idx]

Mypy告诉你还没有处理start为None的情况。

$ mypy player_order.py

player_order.py:8: error: Argument 1 to "index" of "list" has incompatible

                      type "Optional[str]"; expected "str"

也可以使用以下操作,声明参数start的类型。

def player_order(names: Sequence[str], start: str = None) -> Sequence[str]:

...

如果你不想 Mypy 出现报错,你可以使用命令

--no-implicit-optional

Example: The Object(ive) of the Game

接下来我们会重写上面的扑克牌游戏,让它看起来更面向对象,以及适当的使用注解。

将我们的纸牌游戏翻译成以下几个类, Card, Deck, Player, Game ,下面是代码实现。

game.py

import random

import sys

class Card:

SUITS = "  ".split()RANKS = "2 3 4 5 6 7 8 9 10 J Q K A".split()def __init__(self, suit, rank):self.suit = suitself.rank = rankdef __repr__(self):return f"{self.suit}{self.rank}"

class Deck:

def __init__(self, cards):self.cards = cards@classmethoddef create(cls, shuffle=False):"""Create a new deck of 52 cards"""cards = [Card(s, r) for r in Card.RANKS for s in Card.SUITS]if shuffle:random.shuffle(cards)return cls(cards)def deal(self, num_hands):"""Deal the cards in the deck into a number of hands"""cls = self.__class__return tuple(cls(self.cards[i::num_hands]) for i in range(num_hands))

class Player:

def __init__(self, name, hand):self.name = nameself.hand = handdef play_card(self):"""Play a card from the player's hand"""card = random.choice(self.hand.cards)self.hand.cards.remove(card)print(f"{self.name}: {card!r:<3}  ", end="")return card

class Game:

def __init__(self, *names):"""Set up the deck and deal cards to 4 players"""deck = Deck.create(shuffle=True)self.names = (list(names) + "P1 P2 P3 P4".split())[:4]self.hands = {n: Player(n, h) for n, h in zip(self.names, deck.deal(4))}def play(self):"""Play a card game"""start_player = random.choice(self.names)turn_order = self.player_order(start=start_player)# Play cards from each player's hand until emptywhile self.hands[start_player].hand.cards:for name in turn_order:self.hands[name].play_card()print()def player_order(self, start=None):"""Rotate player order so that start goes first"""if start is None:start = random.choice(self.names)start_idx = self.names.index(start)return self.names[start_idx:] + self.names[:start_idx]

if name == "main":

# Read player names from command lineplayer_names = sys.argv[1:]game = Game(*player_names)game.play()

好了,下面让我们添加注解

Type Hints for Methods

方法的类型提示与函数的类型提示非常相似。唯一的区别是self参数不需要注释,因为它是一个类的实例。Card类的类型很容易添加:

class Card:

SUITS = "  ".split()RANKS = "2 3 4 5 6 7 8 9 10 J Q K A".split()def __init__(self, suit: str, rank: str) -> None:self.suit = suitself.rank = rankdef __repr__(self) -> str:return f"{self.suit}{self.rank}"

注意:init() 的返回值总是为None

Class作为类型

类别和类型之间有对应关系。例如,Card的所有实例一起形成Card类型。要使用类作为类型,只需使用类的名称Card。

例如:Deck(牌组)本质上由一组Card对象组成,你可以像下面这样去声明

class Deck:

def __init__(self, cards: List[Card]) -> None:self.cards = cards

但是,当您需要引用当前定义的类时,这种方法就不那么有效了。例如,Deck.create() 类方法返回一个带有Deck类型的对象。但是,您不能简单地添加-> Deck,因为Deck类还没有完全定义。

这种情况下可以在注释中使用字符串文字。就像下面使用"Deck",声明了返回类型,然后加入docstring注释进一步说明方法。

class Deck:

@classmethoddef create(cls, shuffle: bool = False) -> "Deck":"""Create a new deck of 52 cards"""cards = [Card(s, r) for r in Card.RANKS for s in Card.SUITS]if shuffle:random.shuffle(cards)return cls(cards)

Player类也可以直接使用 Deck作为类型声明. 因为在前面我们已经定义它

class Player:

def __init__(self, name: str, hand: Deck) -> None:self.name = nameself.hand = hand

通常,注释不会在运行时使用。这为推迟对注释的评估提供了动力。该提议不是将注释评估为Python表达式并存储其值,而是存储注释的字符串表示形式,并仅在需要时对其进行评估。

这种功能计划在Python 4.0中成为标准。但是,在Python 3.7及更高版本中,可以通过导入future属性的annotations来实现:

from future import annotations

class Deck:

@classmethoddef create(cls, shuffle: bool = False) -> Deck:...

使用 future之后就可以使用Deck对象替换字符串"Deck"了。

返回 self 或者 cls

如前所述,通常不应该注释self或cls参数。在一定程度上,这是不必要的,因为self指向类的实例,所以它将具有类的类型。在Card示例中,self拥有隐式类型Card。此外,显式地添加这种类型会很麻烦,因为还没有定义该类。所以需要使用字符串“Card”声明返回类型。

但是,有一种情况可能需要注释self或cls。考虑如果你有一个其他类继承的超类,并且有返回self或cls的方法会发生什么:

dogs.py

from datetime import date

class Animal:

def __init__(self, name: str, birthday: date) -> None:self.name = nameself.birthday = birthday@classmethoddef newborn(cls, name: str) -> "Animal":return cls(name, date.today())def twin(self, name: str) -> "Animal":cls = self.__class__return cls(name, self.birthday)

class Dog(Animal):

def bark(self) -> None:print(f"{self.name} says woof!")

fido = Dog.newborn("Fido")

pluto = fido.twin("Pluto")

fido.bark()

pluto.bark()

运行上面的代码,Mypy会抛出下面的错误:

$ mypy dogs.py

dogs.py:24: error: "Animal" has no attribute "bark"

dogs.py:25: error: "Animal" has no attribute "bark"

问题是,即使继承的Dog.newborn()和Dog.twin()方法将返回一个Dog,注释表明它们返回一个Animal。

在这种情况下,您需要更加小心以确保注释正确。返回类型应与self的类型或cls的实例类型匹配。这可以使用TypeVar来完成,这些变量会跟踪实际传递给self和cls的内容:

dogs.py

from datetime import date

from typing import Type, TypeVar

TAnimal = TypeVar("TAnimal", bound="Animal")

class Animal:

def __init__(self, name: str, birthday: date) -> None:self.name = nameself.birthday = birthday@classmethoddef newborn(cls: Type[TAnimal], name: str) -> TAnimal:return cls(name, date.today())def twin(self: TAnimal, name: str) -> TAnimal:cls = self.__class__return cls(name, self.birthday)

class Dog(Animal):

def bark(self) -> None:print(f"{self.name} says woof!")

fido = Dog.newborn("Fido")

pluto = fido.twin("Pluto")

fido.bark()

pluto.bark()

在这个例子中有几个需要注意的点:

类型变量TAnimal用于表示返回值可能是Animal的子类的实例。.

我们指定Animal是TAnimal的上限。指定绑定意味着TAnimal将是Animal子类之一。这可以正确限制所允许的类型。

typing.Type []是type()的类型。需要注意,是cls的类方法需要使用这种形式注解,而self就不用使用。

注解 *args 和 **kwargs

在面向对象的游戏版本中,我们添加了在命令行上命名玩家的选项。这是通过在程序名称后面列出玩家名称来完成的:

$ python game.py GeirArne Dan Joanna

Dan: A Joanna: 9 P1: A GeirArne: 2

Dan: A Joanna: 6 P1: 4 GeirArne: 8

Dan: K Joanna: Q P1: K GeirArne: 5

Dan: 2 Joanna: J P1: 7 GeirArne: K

Dan: 10 Joanna: 3 P1: 4 GeirArne: 8

Dan: 6 Joanna: Q P1: Q GeirArne: J

Dan: 2 Joanna: 4 P1: 8 GeirArne: 7

Dan: 10 Joanna: 3 P1: 3 GeirArne: 2

Dan: K Joanna: 5 P1: 7 GeirArne: J

Dan: 6 Joanna: 9 P1: J GeirArne: 10

Dan: 3 Joanna: 5 P1: 9 GeirArne: Q

Dan: A Joanna: 9 P1: 10 GeirArne: 8

Dan: 6 Joanna: 5 P1: 7 GeirArne: 4

关于类型注释:即使名称是字符串元组,也应该只注释每个名称的类型。换句话说,您应该使用字符串而不是元组[字符串],就像下面这个例子:

class Game:

def __init__(self, *names: str) -> None:"""Set up the deck and deal cards to 4 players"""deck = Deck.create(shuffle=True)self.names = (list(names) + "P1 P2 P3 P4".split())[:4]self.hands = {n: Player(n, h) for n, h in zip(self.names, deck.deal(4))}

类似地,如果有一个接受**kwargs的函数或方法,那么你应该只注释每个可能的关键字参数的类型。

Callables可调用类型

函数是Python中的一类对象。可以使用函数作为其他函数的参数。这意味着需要能够添加表示函数的类型提示。

函数以及lambdas、方法和类都由type的Callable对象表示。参数的类型和返回值通常也表示。例如,Callable[[A1, A2, A3],Rt]表示一个函数,它有三个参数,分别具有A1、A2和A3类型。函数的返回类型是Rt。

在下面这个例子, 函数 do_twice() 传入一个Callable类型的func参数,并指明传入的函数的参数类型为str,返回值类型为str。比如传入参数create_greeting.

do_twice.py

from typing import Callable

def do_twice(func: Callable[[str], str], argument: str) -> None:

  print(func(argument))print(func(argument))

def create_greeting(name: str) -> str:

return f"Hello {name}"

do_twice(create_greeting, "Jekyll")

Example: Hearts

让我们以红心游戏的完整例子来结束。您可能已经从其他计算机模拟中了解了这个游戏。下面是对规则的简要回顾:

四名玩家每人玩13张牌。

持有2的玩家开始第一轮,必须出2。

如果可能的话,玩家轮流打牌,跟随领头的一套牌。

在第一套牌中打出最高牌的玩家赢了这个把戏,并在下一个回合中成为开始牌的玩家。

玩家不能用,除非已经在之前的技巧中玩过。

玩完所有牌后,玩家如果拿到某些牌就会获得积分:

Q为13分

每个1为分

一场比赛持续几轮,直到得到100分以上。得分最少的玩家获胜

具体游戏规则可以网上搜索一下.

在这个示例中,没有多少新的类型概念是尚未见过的。因此,我们将不详细讨论这段代码,而是将其作为带注释代码的示例。

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. Day 2-3 虚拟化、系统安装&常见系统命令

    系统安装-VMware Workstations,注意事项:a/ 创建虚拟机,安装时,选择“稍后安装操作系统”的目的避免默认最小化安装,需要定制化安装;;b/虚拟机内存分配原则:| 物理机器 | 虚拟机 | | 2GB |1GB || >=4GB | 2GB | c/在Linux安装时,忘记了选择时区,然…...

    2024/4/16 14:49:07
  2. kickstart自动安装脚本

    kichstart制作 在系统安装成功后,已经生成当前系统在安装过程当中所有问题答案的自动安装脚本 :ls anaconda_ks.cfg安装系统时,需要把文件 anaconda_ks.cfg中的东西手动写好(过于复杂,所以使用kickstart命令)eg:等等,##################1.安装软件的制作工具######…...

    2024/4/24 8:34:52
  3. Centos7 yum安装mysql5.7,查看默认root密码

    CentOS7默认安装MariaDB,安装mysql5.7就需要添加mysql官方yum源 1. 下载官方yum源 首先需要下载官方yum源 wget https://repo.mysql.com/mysql57-community-release-el7.rpm 2.安装yum源 yum -y install mysql57-community-release-el7.rpm 3.安装mysql服务器 一切准备就绪,现…...

    2024/4/24 8:34:51
  4. kafka生产数据不能均匀到每个分区

    kafka生产者数据不均匀,不能均匀分配数据到每个分区,这样容易造成消费积压,造成这个的原因是因为生产者使用了固定的key导致解决方案如果设定key,key不要用几个固定的常量,没有key,会随机分发到分区;会均匀;建议有业务含义的值,例如:订单号正常情况下,应该是数据均匀在…...

    2024/4/24 8:34:50
  5. 网络流量分析利器-可视化网络-netflow【3】-netflow版本5和版本9区别

    网络流量分析利器-可视化网络-netflow【1】-基础原理 网络流量分析利器-可视化网络-netflow【2】-Cisco NetFlow 工作原理介绍及配置 网络流量分析利器-可视化网络-netflow【3】-netflow版本5和版本9区别 网络流量分析利器-可视化网络-netflow【4】-接收器nfdump简介 网络流…...

    2024/4/24 8:34:49
  6. Liunx 文件的查找

    脚本文件的查找 Which命令which命令的作用是,在PATH变量指定的路径中,搜索某个系统命令的位置,并且返回第一个搜索结果。也就是说,使用which命令,就可以看到某个系统命令是否存在,以及执行的到底是哪一个位置的命令。语法: which [-a] command-a: 将所有由path目录中可以…...

    2024/4/24 8:34:49
  7. 安全故障导致CPU偏高问题处理

    疫情在全世界肆无忌惮蔓延,但在我大中华国内得到有效控制,这不仅仅体现一个国家的综合实力也体现我们大中华亿万民心团结,才能一次一次的战胜住在外界看起来各种不可抗击的力量,国之战神在于民心,团结力量大,而我们做为IT行业运维技术人员,自身不仅要具备实战技术好,也…...

    2024/4/24 8:34:47
  8. mysql 5.7 二进制普通安装

    1 官网下载mysql-5.7.29-linux-glibc2.12-x86_64.tar.gz 2 可以先检查系统中的Mysql或mariadb,如果存在将其卸载[root@bogon ~]# rpm -qa |grep mysqlphp-mysql-5.4.16-46.1.el7_7.x86_64[root@bogon ~]# rpm -qa |grep mariadbmariadb-libs-5.5.64-1.el7.x86_64mariadb-serve…...

    2024/4/24 8:34:46
  9. (二)搭建一个完成的Kubernetes/K8s集群v.1.16

    单节点集群多节点集群 注意node通过连接loadbalancer 转发到mateter 的apiserver来进行运作的 集群规划:角色 ip 组件K8S-master1 192.168.0.101 kube-apiserver kube-controller-manager kube-scheduleretcdK8S-master2 192.168.0.102 kube-apiserver kube-controller-manag…...

    2024/4/24 8:34:48
  10. Linux 文本处理利器--Awk常用命令

    简介:在 Linux/UNIX 系统中,awk 是一个功能强大的编辑工具,逐行读取输入文本,并根据指定的匹配模式进行查找,对符合条件的内容进行格式化输出或者过滤处理,可以在无交互的情况下实现相当复杂的文本操作,被广泛应用于 Shell 脚本,完成各种自动化配置任务。工作原理逐行读…...

    2024/4/24 8:34:45
  11. RedHat 第25期在线培训班---第8课

    一、知识点:磁盘容量配额:xfs_quota:dequota: 软硬链接: RAID: 磁盘阵列组:降低硬盘损坏丢失书架的几率;提高硬盘读写速度。 mount:mount /dev/sdb2 /backup umount: umount /dev/sdb2二、 添加硬盘设备:(添加硬盘,分区,格式化,挂载)1 fdisk /dev/sdb12 file …...

    2024/4/24 8:34:43
  12. linux-5week

    1、查找/etc目录下大于1M且类型为普通文件的所有文件find:find /data -name "f*" -a -type ffind [OPTION]... [查找路径] [查找条件] [处理动作]查找路径:指定具体目标路径;默认为当前目录查找条件:指定的查找标准,可以文件名、大小、类型、权限等标准进行;默认…...

    2024/4/16 14:49:37
  13. # IT明星不是梦 # Ceph持久化存储为k8s应用提供存储方案(1)

    目录:1)Ceph介绍2)部署Ceph集群采用版本【2020最新nautilus (stable)14.2.7】 为什么使用Ceph? ceph其实早在2004年的时候写的第一行代码,它是到2012年才有的第一个开发版本,到2014年的时候ceph才慢慢有公司在生产上应用了,那时候一般有的版本也不是很稳定,也是进行一些…...

    2024/4/17 6:36:22
  14. 查询数据库内历史同期和总计脚本

    一、需求 1. 需求背景需求一:统计当前及历史同期业务量并在一个字段内展示; 需求二:统计指定时间段内业务总量;2. 最终效果展示 2.1 模拟表的表结构 本文以水果销量模拟真实的业务量2.2 当前及历史同期效果展示 统计近7天内和历史同期各水果销量2.3 某时间段内总计效果展示…...

    2024/4/16 14:49:12
  15. Spring Cloud Eureka与Zookeeper作为注册中心的取舍

    近日刷微信看到了一篇被大量转载的文案——《京东面试官让你谈谈 zookeeper 和 eureka 哪个更好使》。说实话,近几年我并未全心全意地参与过Java开发项目,一直以底层平台运维和提供解决方案的角色出现,暂且从运维人的角度来回答一下这个问题。从文案的材料看,这是一个微服务…...

    2024/4/19 15:35:38
  16. CentOS下安装man手册

    最小化安装CentOS后有一些帮助文件安装的不全,可以通过以下方式进行安装:yum install man-pages...

    2024/4/16 14:49:37
  17. NTP时间服务器

    linux NTP服务端配置 NTP服务器【Network Time Protocol(NTP)】是用来使计算机时间同步化的一种协议,它可以使计算机对其服务器或时钟源(如石英钟,GPS等等)做同步化,它可以提供高精准度的时间校正(LAN上与标准间差小于1毫秒,WAN上几十毫秒),且可介由加密确认的方式来…...

    2024/4/16 14:49:12
  18. Linux系统目录和常用文件管理命令

    Linux系统命名规则1,文件名最长255字节2,包括路径在内文件名称最长4095字节3,蓝色-->目录 绿色-->可执行文件 红色-->压缩文件 浅蓝色-->链接文件 灰色-->其他文件4,除了斜杠和null,所有字符都有效,但使用特殊字符的目录名和文件不推荐使用,有些字符需要…...

    2024/4/16 14:49:07
  19. 2019 年全球云计算市场份额出炉

    当今的全球云计算市场风起云涌,呈现出一种群雄逐鹿的格局。虽然AWS继续主导全球云基础设施服务市场,但是,它们的市场份额实际上已经开始下降。根据研究公司Canalys的最新调研报告,AWS 以32.3%的份额雄踞第一、其后分别是Azure 16.9%、谷歌云 5.8%、阿里云 4.9%。这其中谷歌…...

    2024/4/16 14:50:03
  20. iPerf 测速软件centos7安装

    Iperf 是一个网络性能测试工具。Iperf可以测试最大TCP和UDP带宽性能,具有多种参数和UDP特性,可以根据需要调整,可以报告带宽、延迟抖动和数据包丢失。使用环境:VPS是centos7_64位系统标准安装,如果提示没有wget、rpm、yum命令请自行安装,系统update的iperf是3.1.3版本,默…...

    2024/4/24 8:34:42

最新文章

  1. 秒翻-网页翻译最佳选择

    使用方法&#xff1a; 安装“沉浸式翻译” 在扩展设置页面勾选“Beta”特性。 输入 DeepLX 现成的 API-https://api.deeplx.org/translate。...

    2024/5/8 13:40:16
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/5/7 10:36:02
  3. jQuery(一)

    文章目录 1. 基本介绍2.原理示意图3.快速入门1.下载jQuery2.创建文件夹&#xff0c;放入jQuery3.引入jQuery4.代码实例 4.jQuery对象与DOM对象转换1.基本介绍2.dom对象转换JQuery对象3.JQuery对象转换dom对象4.jQuery对象获取数据获取value使用val&#xff08;&#xff09;获取…...

    2024/5/7 6:19:10
  4. JavaEE 初阶篇-生产者与消费者模型(线程通信)

    &#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 生产者与消费者模型概述 2.0 在生产者与消费者模型中涉及的关键概念 2.1 缓冲区 2.2 生产者 2.3 消费者 2.4 同步机制 2.5 线程间通信 3.0 实现生产者与消费者模…...

    2024/5/7 4:40:37
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/8 6:01:22
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/7 9:45:25
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/5/4 23:54:56
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/7 14:25:14
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/5/4 23:54:56
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/5/4 23:55:05
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/5/4 23:54:56
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/5/7 11:36:39
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/5/4 23:54:56
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/6 1:40:42
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/5/4 23:54:56
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/5/4 23:55:17
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/5/7 9:26:26
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/5/4 23:54:56
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/4 23:55:06
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/5 8:13:33
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/5/4 23:55:16
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/5/4 23:54:58
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/6 21:42:42
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/5/4 23:54:56
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57