2024年最新python类型检测最终指南--Typing的使用_typing_extensions(1),我阿里P7了解到的Python面试的一些小内幕

一、Python所有方向的学习路线

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照下面的知识点去找对应的学习资源,保证自己学得较为全面。

img
img

二、Python必备开发工具

工具都帮大家整理好了,安装就可直接上手!img

三、最新Python学习笔记

当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。

img

四、Python视频合集

观看全面零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。

img

五、实战案例

纸上得来终觉浅,要学会跟着视频一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。img

六、面试宝典

在这里插入图片描述

在这里插入图片描述

简历模板在这里插入图片描述

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

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

  • 类型注解和提示(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部分,当计算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.pyheadlines.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.pyPython Type Checking--------------------oooooooooooooooooooo Use Mypy oooooooooooooooooooo

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

Pros and Cons

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

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

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

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

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

  • 如果您刚开始学习Python,可以安全地等待类型提示,直到您有更多经验。
  • 类型提示在短暂抛出脚本中增加的价值很小。
  • 在其他人使用的库中,尤其是在PyPI上发布的库中,类型提示会增加很多价值。使用库的其他代码需要这些类型提示才能正确地进行类型检查。
  • 在较大的项目中,类型提示可以帮助您理解类型是如何在代码中流动的,强烈建议您这样做。在与他人合作的项目中更是如此。

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

注解

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

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

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

函数注解

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

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.pyreveal.py:4: error: Revealed type is 'builtins.float'
reveal.py:8: error: Revealed local types are:reveal.py:8: error: circumference: builtins.floatreveal.py:8: error: radius: builtins.int

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

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

变量注解

有时类型检查器也需要帮助来确定变量的类型。变量注释在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>>> nothingNameError: name 'nothing' is not defined
>>> __annotations__{'nothing': <class 'str'>}

类型注解

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

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

import math
def circumference(radius):    # type: (float) -> float    return 2 * math.pi * radius

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

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

>>> circumference.__annotations__{}

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

def headline(text, width=80, fill_char="-"):    # type: (str, int, str) -> str    return f" {text.title()} ".center(width, fill_char)
print(headline("type comments work", width=40))

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

# headlines.py   def headline(      text,           # type: str      width=80,       # type: int      fill_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.pyheadline.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中使用,代价是必须维护第二组文件。通常,如果无法更改原始源代码,则只需使用存根文件。

Playing With Python Types, 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.pyP4: ♣9 ♢9 ♡2 ♢7 ♡7 ♣A ♠6 ♡K ♡5 ♢6 ♢3 ♣3 ♣QP1: ♡A ♠2 ♠10 ♢J ♣10 ♣4 ♠5 ♡Q ♢5 ♣6 ♠A ♣5 ♢4P2: ♢2 ♠7 ♡8 ♢K ♠3 ♡3 ♣K ♠J ♢A ♣7 ♡6 ♡10 ♠KP3: ♣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 还包括其他的很多类型比如 CounterDequ``eFrozenSetNamedTuple, 和 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元组编写元组[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__()等方法。

类型别名

使用嵌套类型(如卡片组)时,类型提示可能会变得非常麻烦。你可能需要仔细看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]
>>> Decktyping.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.pyplay.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 empty     while 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.pyP3: ♢10  P4: ♣4   P1: ♡8   P2: ♡QP3: ♣8   P4: ♠6   P1: ♠5   P2: ♡KP3: ♢9   P4: ♡J   P1: ♣A   P2: ♡AP3: ♠Q   P4: ♠3   P1: ♠7   P2: ♠AP3: ♡4   P4: ♡6   P1: ♣2   P2: ♠KP3: ♣K   P4: ♣7   P1: ♡7   P2: ♠2P3: ♣10  P4: ♠4   P1: ♢5   P2: ♡3P3: ♣Q   P4: ♢K   P1: ♣J   P2: ♡9P3: ♢2   P4: ♢4   P1: ♠9   P2: ♠10P3: ♢A   P4: ♡5   P1: ♠J   P2: ♢QP3: ♠8   P4: ♢7   P1: ♢3   P2: ♢JP3: ♣3   P4: ♡10  P1: ♣9   P2: ♡2P3: ♢6   P4: ♣6   P1: ♣5   P2: ♢8

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

The Any Type

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

import randomfrom 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.pychoose.py:10: error: Revealed type is 'builtins.list[builtins.str*]'choose.py:13: error: Revealed type is 'Any'

文末有福利领取哦~

👉一、Python所有方向的学习路线

Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。img

👉二、Python必备开发工具

img
👉三、Python视频合集

观看零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。
img

👉 四、实战案例

光学理论是没用的,要学会跟着一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。(文末领读者福利)
img

👉五、Python练习题

检查学习结果。
img

👉六、面试资料

我们学习Python必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。
img

img

👉因篇幅有限,仅展示部分资料,这份完整版的Python全套学习资料已经上传

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值