Python3 - 开始python编程(五)

Python3 - 开始python编程(五)

了解代码层次结构以及如何使代码可重用。在上一篇文章中,我们介绍了 if 语句,while 循环和 for 循环。

其中一些”if”语句非常长,如果我们想在该”if”语句中做多个事情怎么办?它们将变得难以阅读。这就是今天的内容。我们开始吧。

函数

实际上,您可以使用 Python 编写没有任何功能的程序,但通常不应该这样做。我们将在范围部分中对此进行更多讨论,但是在global scope处编写的代码将用作程序的entry point。入口点只是一种说法,这是程序启动时首先运行的代码。很容易,对吧?

我们编写函数是因为它破坏了我们的代码,使其更易于阅读。我们可以遵循一些编程原则,这些原则实际上只是使您的工作更轻松的准则。这不是一个详尽的列表,但足以让您考虑如何改善代码:

  • 分离原则-如果您对一个字符串中的字母计数,然后将两个数字相加,那么使用这两个单独的函数一个函数对字符串中的字母计数,而另一个对数字进行加法比较有意义。
  • 单一责任原则-概括为:每件事都有一个任务,而且每件事都做得很好。
  • 不要重复自己(DRY)-这很明显:为什么要多次输入相同的文本?为什么要多次输入相同的文本?如果您是 A 型性格,这会让您发疯,但是当 A 型开发人员看到一遍又一遍写相同的东西时,他们就会发疯。

我们将在以后再讲更多,但是现在,这是一个很好的基础。

1
2
3
4
5
6
7
8
9
10
11
# Define the function
def add(first_number: int, second_number: int) -> int:
    result = firstNumber + secondNumber
    return result

# Call the function
result = add(5, 3)
# result is equal to 8

result = add(first_number=10, second_Number=result)
# result is now equal to 18

首先,向您展示一个用 Swift 编写的函数的示例。语法可能看起来很奇怪,但我们将介绍所有内容。

首先,我们使用def关键字定义一个函数,它是define的缩写。

接下来,我们给函数命名为”add”。

函数名称后带有括号。如果没有参数,则将其留空,如下所示: def do_stuff():

如果有参数(可以将其传递到函数中的东西),则可以将它们包含在声明中。在上面的示例中,我们确实有两个实参,分别是”firstnumber”和”second_number”。这些在示例中不是很具描述性,但是应该_。

函数的body中将使用”first_number”和”second_number”。函数的主体是所有在函数声明下方缩进四个空格的代码。不要忘记函数使用冒号来启动函数的主体。

接下来,我们有这部分first_number:i_nt_ . Int 是我们在调用此函数时期望使用的类型。 “second_number:int”也是如此。

最后,在括号之外,最后还有这种奇怪的语法。 -> int

这仅意味着当该函数结束时,我们应该_返回一个整数值。 Return 只是说退出此函数并返回 return 右边的任何值的一种方式。在上面的示例中,我们创建了一个名为“结果”的常量,该常量是”first_number”和”second_number”的和。我们可以将结果称为“任何”,但这是有道理的,然后我们可以“返回结果”。

我们称我们实际上使用的是result = ...函数,因为我们需要记住从 fu 返回的常量或变量中的值。连接。如果我们的函数不应该返回任何内容,则会出现错误。如果我们的函数应该返回某个值,而我们没有capture该值,则不会收到警告-相反,该函数将返回`None’。这可能会在以后引起问题,因此请始终记住在需要时从函数中返回一个值。

如果我们不在乎返回的值,我们也可以使用下划线忽略它们。 _ = add(4,3)执行 add 函数,但在函数完成时不保存返回值。

我们可以省略函数参数中的:int以及 -> int结尾。这些只是为了确保参数仅允许使用特定类型或在修改函数后始终应返回特定类型的安全预防措施。

我们也可以从函数主体中删除result,因为我们只需要first_number + second_number的结果。让我们看看这些更改后的外观:

1
2
def add(first_number, second_number):
    return firstNumber + secondNumber

至少对我来说看起来更好。在大型程序中,我不能过分强调强类型参数和返回值的重要性,并希望您始终在函数中使用type hints。不仅是为了您的理智,还为了可怜的灵魂的理智,后者在五年后挖掘了您的代码以对其进行更改。

您可以看到描述性名称如何派上用场。如果您在变量名称方面遇到困难,请记住以下引号:

“计算机科学中只有两件难事:缓存无效和命名。” —菲尔·卡尔顿

帮助我在函数中命名参数的原因是问自己:“这对其他人来说是否适合作为句子?” _我的意思是,是否可以用诸如”with”,”to”,“来自`或类似的介词,这有意义吗?让我们在”add”功能上测试一下这一理论。

将第二个数字添加到第一个数字。

这对我来说很有意义,所以我认为它可以通过测试。

我不会说这是一个完美的示例,但是您应该努力做到代码clarity,尤其是函数中的代码_clarity,因此请不断考虑这一点。

因此,我们以几种不同的方式编写了函数。每种方法都是正确的。您可以完全控制它的读取方式以及将与之一起使用的参数名称和变量。


打印语句 print(“Hello,World!”)实际上是一个返回 String 值的函数。我们使用它在console中显示变量(可以将其视为只读终端窗口或命令提示符)。真正很酷的事情是它知道如何将一种类型转换为或转换为字符串,例如将整数转换为字符串,甚至是字典。我不会说它看起来总是很漂亮,因为有时看起来不会,但是在构建程序和debugging期间会广泛使用它。调试程序只是发生问题的另一种方式,现在我们试图找出问题出在哪里。

稍后,我们将通过一个有趣的故事来讨论更多有关调试的信息,但就目前而言,让我们坚持打印声明以弄清楚我们的问题是什么:

1
2
3
4
5
6
7
def add(first_number: int, second_number: int) -> int {
    return firstNumber + secondNumber
}

result = add(5, 25)

print(result)

从现在开始,我将使用print("something")而不是 #结果等于“某物”,因为它是您期望在实际程序中使用它的方式。


函数不一定必须返回值。相反,我们可以嵌套 print 语句来为我们处理错误日志,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
def something_went_wrong():
    print("Aww...")

if a < b:
    if sun_is_shining:
        if made_cup_of_coffee:
            print("Yay!")
        else:
            something_went_wrong()
    else:
        something_went_wrong()
else:
    something_went_wrong()

现在,我们已经将print("Aww ...")并封装到一个函数中,可以在需要的任何时候调用它。这称为 decomposition。如果 composition 正在构建一段代码的工作方式,则分解就是一次又一次地提取重复使用的代码,并使其更易于引用。对于这个简单的示例,我们只需要编写 print 语句,因为它只有一行,但是为了简洁起见,也许有些懒惰,这足以理解这个想法。

由于此函数不返回值,所以当它完成时,我们不会have使用 return 关键字。我们can使用了返回值,但是在执行完体内的所有代码并且到达了右花括号时,该函数仍会返回。唯一可能在不返回值的函数中使用 return 关键字的情况是,当您要退出函数before时,所有代码都已运行。 # reach this code.

1
2
注意:如果您在条件检查之外放入return语句,则某些编辑器(例如PyCharm)将声明“此代码可能永远不会执行"或类似的内容。只是提示:“我们知道您已经这样做了,但这是您清理不使用的代码或修复return语句的机会,以便下面的代码可以运行。"

def print_disposition(is_sunny: bool): if is_sunny: print(“Yay!”) return # early exits from the function

1
print("Aww...")        # if it is sunny the program will never

“那么鲍勃吉他怎么了?”我不仅无缘无故地将吉他放在页面顶部。我想举一个现实的例子,说明函数应该如何工作。

想象一下,如果您愿意的话,那把吉他可以完美地调到 A440,即 ConcertA。

如果我要演奏第一弦(t(底部的 E 字符串),您会听到完美的 E 音符。无论我摘取第一个字符串多少次,您都将始终听到同样的完美 E。这是一个函数示例。当我们弹奏琴弦时,它会以一定的频率振动并返回声音。这可能与主体和函数的返回有关。

通常的想法是,我们不在乎函数是如何填充的,我们只是希望它能够像调用它时所期望的那样工作。

还有一件事…

如果您知道要使用某个函数,但还没有弄清楚该怎么做,则可以使用pass关键字跳过该函数的执行。这也可以在if语句中使用。作为一个简单的示例,请查看此功能的开始:

1
2
def calculate_driving_distance(starting_city, ending_city):
    pass

Annie Spratt摄于Unsplash

作用域

最后一部分:作用域。到目前为止,我们已经经历了作用域。我们通过函数,if 语句,while 循环和 for 循环来体验它。从一般意义上讲,我们一直都在讨论它。那么作用域是什么?

作用域定义了对象在代码中有效范围。

“好吧鲍勃,你疯了,你只是告诉我一个无生命的物体有生命。”不,在程序中,我们使它们栩栩如生,我们使它们执行工作。为什么不这样考虑:

代码中的对象具有生命周期。自开始以来我们所做的大部分工作都是在程序启动时创建变量,而在程序结束时变量将消失。这被认为是对象的寿命。

创建对象时,它只是一个通用名称,包含变量和常量之类的东西,它具有设定的寿命。对象的寿命取决于其在程序中的放置位置。如果您将一个对象自己放置在诸如 my_object:str =”Hello World”之类的程序中,则该对象将在程序运行时一直存在。一旦程序停止,就会从内存中删除”my_object”。

问题是我们不能将所有内容都加载到内存中。我们会用光空间,因此我们通过定义程序的范围来选择在程序中任何给定位置应使用的内容。

因此,我已经讨论了什么是作用域,但是让我们看一些实际的示例,这些示例说明了如何在代码中定义和使用作用域,以及如何使您考虑将变量放在何处:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
my_global_integer = 1

def my_global_function():
    name: str = "Jimmy"
    print(name)

def my_global_returning_function() -> str:
    return "Katy"

global_name = my_global_returning_function()

if global_name == "Katy":
    print(global_name)
else:
    my_magic_number = 42
    print(my_global_integer)
    print(my_global_function()

我在其中定义了各种级别的作用域,因此让我们从头开始。

“my_global_integer”是一个对象,只要程序仍在运行,就可以在代码中的任何位置使用它。

“my_global_function”是一个函数,只要程序仍在运行,它就可以在代码中的任何位置使用,但是,主体内部的”name”变量仅在主体内部使用。该函数退出后,”name”变量及其值将从内存中删除。

如果要在函数外部使用variable_variable,Python 将识别该变量并在运行时自动创建全局变量。这可能会导致内存效率问题。但是,如果在一个函数中定义了”_name“,然后又在另一个函数中定义了它,则将在每个函数的局部函数作用域中使用它。

“my_global_returning_function”与”my_global_function”完全相同,除了它返回一个值,您可能会很想知道它返回在主体内部创建的变量,因为那是它返回的结果,但实际上它仅返回“值” **的变量。当函数到达主体的末尾时,变量“名称”将从内存中删除,而您只有希望复制给另一个变量的值的副本。

因为我们知道返回函数返回”Katy”,所以该”if”语句将比较”if”“Katy” ==”Katy”,是的,它们是相同的!因此,我们从全局范围中引用回”global_name”变量,并从那里获取值(因为在执行评估之前我们已分配了一个值)。

但是,假设返回的名称是诸如“”Sophie”`之类的名称,与”Katy”之类的名称不同。在”else”语句中,既有常量也有变量被创建和初始化。仅当我们位于 else 语句的花括号中时,才可以使用它们。如果要在 if 语句的 if 部分中创建一个变量,则只能在 if 部分中使用它。

我们仍然可以打印”my_global_variable”和”my_global_function”,因为它们是在主体外部定义的。

关于 Python 是一种解释型语言的特殊之处在于,如果我在第 16 行上错误键入my_global_integer并使用myGlobalInteger,则程序仍将运行。

Python 之所以这样做,是因为它甚至不知道它不使用的行。这在编译语言中是不同的,因为所有行都向下编译为 int 字节代码,然后再运行。由于 Python 在运行时进行所有编译,因此它仅编译所需的内容,而跳过其余内容。

如果您忘记了我所说的所有其他内容,请知道您始终可以对变量进行更深入的了解,但永远不能超出最初声明该变量的主体的范围。

试想一下,每当您离开包含变量的身体时,Bones 都会说:“他死了,吉姆!”星际迷航迷不多?好吧,我知道很多人喜欢加利福尼亚酒店,并且它在相同的前提下运作:您永远都不会离开。也不是老鹰迷吗?好吧,我敢肯定,如果您觉得自己需要一个助记符,可以使用自己的助记符来记住作用域。

这就是范围。

摘要

因此,我们意识到到目前为止编写的代码有些草率。

我们从 3 条原则 开始,以帮助我们想象代码的外观:关注点分离,单一责任原则和不重复自己。

然后,我们讨论了“函数”以及它们如何帮助我们遵守这些原则。我强烈建议您练习编写自己的函数,以便掌握它的作用并了解允许和不允许的功能。我们了解了如何使用return提前退出函数,以及如何通过pass完全跳过执行。

最后,我们讨论了“作用域”。只是一个提示,在使用变量的最低作用域内声明变量,然后,当您需要在其他地方可用的变量时,将声明移至更高的作用域。

建议阅读

Python 教程 4.5 至 4.7

4. 更多控制流工具-Python 3. 7.3 文档

下一步是什么

你做到了!接下来,我们将讨论类,属性和方法。

Rating: