| 关于   英文版   «  6.1. 第 5 周:数据结构   ::   目录   ::   8.1. 第 7 周:SQL  »

7.1. 第 6 周:Python

7.1.1. 欢迎!

  • 在前几周,你已接触过编程的基本构建模块。

  • 你学习了使用一种名为 C 的底层编程语言进行编程。

  • 今天,我们将使用一种名为 Python 的高级编程语言。

  • 在学习这门新语言的过程中,你将发现自己更有能力自学新的编程语言。

7.1.2. 你好,Python!

  • 几十年来,人类已经看到如何改进先前编程语言中做出的设计决策。

  • Python 是一种在你已学的 C 语言基础上构建的编程语言。

  • Python 还可以访问大量由用户创建的库。

  • 与作为 编译型语言 的 C 不同,Python 是一种 解释型语言 ,你无需单独编译程序。相反,你在 Python 解释器 中运行程序。

  • 1/hello0.c

    到目前为止,代码看起来是这样的:

    // A program that says hello to the world
    
    #include <stdio.h>
    
    int main(void)
    {
        printf("hello, world\n");
    }
    

    注意这个 C 程序比下面的 Python 版本更复杂。你可以下载此代码 here 。

  • 如今,你会发现编写和编译代码的过程已被简化。

  • 1/hello0.py

    例如,上述代码在 Python 中将呈现为:

    # A program that says hello to the world
    
    print("hello, world")
    

    请注意,分号已消失,且无需任何库。你可以在终端中输入 python hello.py 来运行此程序。你可以下载此代码 here 。

  • Python 能够以相对简单的方式实现那些在 C 语言中相当复杂的操作。

  • 本周的目标之一是 更加适应这种不适感 ,即不知道如何确切地解决问题或正确、符合语法地实现解决方案:在课程学术诚信政策允许的范围内,主动寻找资源来帮助自己!

7.1.3. 拼写检查器

  • 6/speller/dictionary.py

    为了说明这种简洁性,让我们在终端窗口中输入 code dictionary.py ,并编写如下代码:

    # Words in dictionary
    words = set()
    
    def check(word):
        """Return true if word is in dictionary else false"""
        return word.lower() in words
    
    def load(dictionary):
        """Load dictionary into memory, returning true if successful else false"""
        with open(dictionary) as file:
            words.update(file.read().splitlines())
        return True
    
    def size():
        """Returns number of words in dictionary if loaded else 0 if not yet loaded"""
        return len(words)
    
    def unload():
        """Unloads dictionary from memory, returning true if successful else false"""
        return True
    

    请注意,上面有四个函数。在 check 函数中,如果 words 中存在 word ,则返回 True 。这比用 C 语言实现要简单得多!类似地,在 load 函数中,字典文件被打开。对于该文件中的每一行,我们将该行添加到 words 。使用 rstrip ,从添加的单词中移除末尾的换行符。 size 仅返回 words 的 len 或长度。 unload 只需返回 True ,因为 Python 自行处理内存管理。你可以在此处 here 下载此代码。

  • 上述代码说明了高级语言存在的原因:为了简化编程并让你更轻松地编写代码。

  • 然而,速度是一种权衡。由于 C 允许你(程序员)对内存管理做出决策,因此它可能比 Python 运行得更快——这取决于你的代码。C 仅执行你的代码行,而当你调用 Python 的内置函数时,Python 还会执行其底层的所有代码。

  • 你可以在 Python documentation 中了解更多关于函数的信息。

7.1.4. 过滤器

  • 6/filter/blur.py

    为了进一步说明这种简洁性,请在终端窗口中输入 code blur.py 创建一个新文件,并编写如下代码:

    # Blurs an image
    
    from PIL import Image, ImageFilter
    
    # Blur image
    before = Image.open("bridge.bmp")
    after = before.filter(ImageFilter.BoxBlur(1))
    after.save("out.bmp")
    

    请注意,该程序从名为 PIL 的库中导入了模块 Image 和 ImageFilter 。它读取一个输入文件并生成一个输出文件。你可以下载此代码 here 。

  • 6/filter/edges.py

    此外,你可以创建一个名为 edges.py 的新文件,方法如下:

    # Blurs an image
    
    from PIL import Image, ImageFilter
    
    # Find edges
    before = Image.open("bridge.bmp")
    after = before.filter(ImageFilter.FIND_EDGES)
    after.save("out.bmp")
    

    请注意,此代码仅对你的 blur 代码做了微小调整,却产生了截然不同的结果。你可以下载此代码 here 。

  • Python 允许你抽象掉在 C 和其他 低级 编程语言中会复杂得多的编程工作。

  • 使用 Python 的权衡之一是它是一种解释型语言,而不是编译型语言(正如我们在课程前面严格定义的那样)。因此,可能会出现一些(通常非常小的) slowdown,这在编译程序中是意想不到的。

7.1.5. 函数

  • 在 C 语言中,你可能见过如下函数:

    printf("hello, world\n");
    
  • 在 Python 中,你会看到如下函数:

    print("hello, world")
    

7.1.6. 库、模块与包

  • 与 C 语言一样,CS50 库也可在 Python 中使用。

  • 以下函数将特别有用:

    get_float
    get_int
    get_string
    
  • 你可以按如下方式导入 cs50 库:

    import cs50
    
  • 你还可以选择仅从 CS50 库导入特定函数,如下所示:

    from cs50 import get_float, get_int, get_string
    

7.1.7. 字符串

  • 1/hello1.c

    在 C 语言中,你可能记得这段代码:

    // get_string and printf with %s
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        string answer = get_string("What's your name? ");
        printf("hello, %s\n", answer);
    }
    

    注意这个 C 程序如何使用 CS50 库来获取用户输入。你可以下载此代码 here 。

  • 1/hello1.py

    此代码在 Python 中转换为:

    # get_string and print, with concatenation
    
    from cs50 import get_string
    
    answer = get_string("What's your name? ")
    print("hello, " + answer)
    

    你可以通过在终端窗口中执行 code hello.py 来编写此代码。然后,你可以通过运行 python hello.py 来执行此代码。请注意 + 符号如何连接 "hello, " 和 answer 。你可以下载此代码 here 。

  • 同样,这也可以在不进行连接的情况下完成:

    # get_string and print, without concatenation
    
    from cs50 import get_string
    
    answer = get_string("What's your name? ")
    print("hello,", answer)
    

    请注意,print 语句会自动在 hello 语句和 answer 之间创建一个空格。

  • 1/hello2.py

    同样地,你可以将上述代码实现为:

    # get_string and print, with format strings
    
    from cs50 import get_string
    
    answer  = get_string("What's your name? ")
    print(f"hello, {answer}")
    

    请注意花括号如何允许 print 函数插值 answer ,使得 answer 出现在其中。必须使用 f 才能正确包含 answer 的格式化。你可以在此处 here 下载此代码。

7.1.8. 位置参数与命名参数

  • C 语言中的函数如 fread 、 fwrite 和 printf 使用位置参数,即你通过逗号分隔提供参数。作为程序员,你必须记住哪个参数位于哪个位置。这些被称为 位置参数 。

  • 在 Python 中,命名参数 允许你无需考虑位置即可提供参数。

  • 你可以在 documentation 中了解更多关于 print 函数的参数信息。

  • 访问该文档时,你可能会看到以下内容:

    print(*objects, sep=' ', end='\n', file=None, flush=False)
    

    请注意,可以向 print 提供多种对象。当向 print 提供多个对象时,会提供一个空格作为分隔符进行显示。类似地,在 print 语句的末尾会提供一个换行符。

7.1.9. 变量

  • 变量声明也得到简化。在 C 中,你可能会有 int counter = 0; 。在 Python 中,同样的代码行会写作 counter = 0 。你无需声明变量的类型。

  • Python 倾向于使用 counter += 1 来递增一,从而失去了 C 语言中输入 counter++ 的能力。

7.1.10. 类型

  • Python 中的数据类型无需显式声明。例如,你看到上面的 answer 是一个字符串,但我们不必告诉解释器这一点:它自己就能知道。

  • 在 Python 中,常用的类型包括:

    bool
    float
    int
    str
    

    注意 long 和 double 缺失。Python 将处理较大和较小数字应使用的数据类型。

  • Python 中的其他一些数据类型包括:

    range   sequence of numbers
    list    sequence of mutable values
    tuple   sequence of immutable values
    dict    collection of key-value pairs
    set     collection of unique values
    
  • 这些数据类型中的每一种都可以在 C 中实现,但在 Python 中,它们的实现更为简单。

7.1.11. 计算器

  • 1/calculator0.c

    你或许在课程前段见过 calculator.c :

    // Addition with int
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        // Prompt user for x
        int x = get_int("x: ");
    
        // Prompt user for y
        int y = get_int("y: ");
    
        // Perform addition
        printf("%i\n", x + y);
    }
    

    注意这个简易计算器的 C 语言实现。你可以下载此代码 here 。

  • 1/calculator0.py

    我们可以像在 C 语言中那样实现一个简单的计算器。在终端窗口中输入 code calculator.py ,并编写如下代码:

    # Addition with int [using get_int]
    
    from cs50 import get_int
    
    # Prompt user for x
    x = get_int("x: ")
    
    # Prompt user for y
    y = get_int("y: ")
    
    # Perform addition
    print(x + y)
    

    请注意 CS50 库是如何导入的。然后,从用户处获取 x 和 y 。最后,打印结果。请注意,在 C 程序中会看到的 main 函数已完全消失!虽然可以使用 main 函数,但并非必需。你可以下载此代码 here 。

  • 可以移除 CS50 库的辅助功能。请按以下方式修改你的代码:

    # Addition with int [using input]
    
    # Prompt user for x
    x = input("x: ")
    
    # Prompt user for y
    y = input("y: ")
    
    # Perform addition
    print(x + y)
    

    请注意,执行上述代码会导致程序行为异常。为什么会这样?

  • 1/calculator1.py

    你可能已经猜到,解释器将 x 和 y 理解为了字符串。你可以通过使用 int 函数来修复代码,如下所示:

    # Addition with int [using input]
    
    # Prompt user for x
    x = int(input("x: "))
    
    # Prompt user for y
    y = int(input("y: "))
    
    # Perform addition
    print(x + y)
    

    注意 x 和 y 的输入是如何传递给 int 函数的,该函数将其转换为整数。如果不将 x 和 y 转换为整数,字符将会连接。你可以下载此代码 here 。

7.1.12. 条件语句

  • 1/compare3.c

    在 C 语言中,你可能记得这样一个程序:

    // Conditionals, Boolean expressions, relational operators
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        // Prompt user for integers
        int x = get_int("What's x? ");
        int y = get_int("What's y? ");
    
        // Compare integers
        if (x < y)
        {
            printf("x is less than y\n");
        }
        else if (x > y)
        {
            printf("x is greater than y\n");
        }
        else
        {
            printf("x is equal to y\n");
        }
    }
    

    注意 C 语言中条件语句的工作方式。你可以下载此代码 here 。

  • 1/compare3.py

    在 Python 中,它将显示如下:

    # Conditionals, Boolean expressions, relational operators
    
    from cs50 import get_int
    
    # Prompt user for integers
    x = get_int("What's x? ")
    y = get_int("What's y? ")
    
    # Compare integers
    if x < y:
        print("x is less than y")
    elif x > y:
        print("x is greater than y")
    else:
        print("x is equal to y")
    

    请注意,这里不再使用花括号,而是利用缩进。其次, if 语句中使用了冒号。此外, elif 取代了 else if 。在 if 和 elif 语句中也不再需要括号。你可以下载此代码 here 。

  • 1/agree2.c

    进一步考察比较操作,请考虑以下 C 语言代码:

    // Logical operators
    
    #include <cs50.h>
    #include <stdio.h>
    
    int main(void)
    {
        // Prompt user to agree
        char c = get_char("Do you agree? ");
    
        // Check whether agreed
        if (c == 'Y' || c == 'y')
        {
            printf("Agreed.\n");
        }
        else
        {
            printf("Not agreed.\n");
        }
    }
    

    请注意 C 语言中逻辑运算符的工作方式。你可以下载此代码 here 。

  • 1/agree2.py

    上述内容可实现如下:

    # Logical operators
    
    from cs50 import get_string
    
    # Prompt user to agree
    s = get_string("Do you agree? ")
    
    # Check whether agreed
    if s == "Y" or s == "y":
        print("Agreed.")
    else:
        print("Not agreed.")
    

    请注意,C 语言中使用的两个竖线已被 or 取代。事实上,人们往往喜欢 Python,因为它更具可读性。另外,请注意 char 在 Python 中并不存在;取而代之的是使用 str 。你可以下载此代码 here 。

  • 1/agree1.py

    实现相同代码的另一种方法是使用 线性表 :

    # Logical operators, using lists
    
    from cs50 import get_string
    
    # Prompt user to agree
    s = get_string("Do you agree? ")
    
    # Check whether agreed
    if s in ["y", "yes"]:
        print("Agreed.")
    else:
        print("Not agreed.")
    

    注意我们如何能够在 list 中表达多个关键字,如 y 和 yes 。你可以下载此代码 here 。

7.1.13. 面向对象编程

  • 某些类型的值不仅可以拥有属性或特征,还可以包含函数。在 Python 中,这类值被称为 对象

  • 在 C 语言中,我们可以创建一个 struct ,将多个变量关联到一个自定义的数据类型中。在 Python 中,我们不仅能做到这一点,还可以在自定义数据类型中包含函数。当一个函数属于某个特定的 对象 时,它被称为 方法 。

  • 1/agree2.py

    例如,Python 中的 strs 内置了 方法 。因此,你可以按如下方式修改代码:

    # Logical operators, using lists
    
    # Prompt user to agree
    s = input("Do you agree? ").lower()
    
    # Check whether agreed
    if s in ["y", "yes"]:
        print("Agreed.")
    else:
        print("Not agreed.")
    

    注意我们如何使用 s.lower() 来规范化输入,使用的是 strs 的内置 lower 方法。你可以下载此代码 here 。

  • 4/copy5.c

    同样,你可能还记得我们如何在 C 语言中复制字符串:

    // Capitalizes a copy of a string without memory errors
    
    #include <cs50.h>
    #include <ctype.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(void)
    {
        // Get a string
        char *s = get_string("s: ");
        if (s == NULL)
        {
            return 1;
        }
    
        // Allocate memory for another string
        char *t = malloc(strlen(s) + 1);
        if (t == NULL)
        {
            return 1;
        }
    
        // Copy string into memory
        strcpy(t, s);
    
        // Capitalize copy
        if (strlen(t) > 0)
        {
            t[0] = toupper(t[0]);
        }
    
        // Print strings
        printf("s: %s\n", s);
        printf("t: %s\n", t);
    
        // Free memory
        free(t);
        return 0;
    }
    

    注意代码的行数。你可以下载此代码 here 。

  • 4/copy.py

    我们可以在 Python 中按如下方式实现上述内容:

    # Capitalizes a copy of a string
    
    # Get a string
    s = input("s: ")
    
    # Capitalize copy of string
    t = s.capitalize()
    
    # Print strings
    print(f"s: {s}")
    print(f"t: {t}")
    

    请注意,此程序比其在 C 语言中的对应版本短得多。你可以下载此代码 here 。

  • 在本课程中,我们仅对 Python 进行初步介绍。因此,随着学习的深入, Python documentation 将变得尤为重要。

  • 你可以在 Python documentation 中了解更多关于字符串方法的信息。

7.1.14. 循环

  • Python 中的循环与 C 语言非常相似。你可能还记得以下 C 语言代码:

    // Demonstrates for loop
    
    #include <stdio.h>
    
    int main(void)
    {
        for (int i = 0; i < 3; i++)
        {
            printf("meow\n");
        }
    }
    
  • 1/cat3.py

    for 循环在 Python 中可按如下方式实现:

    # Better design
    
    for i in range(3):
        print("meow")
    

    注意 i 从未被显式使用。然而,Python 会递增 i 的值。你可以下载此代码 here 。

  • 1/cat1.py

    此外,一个 while 循环可以按如下方式实现:

    # Demonstrates while loop
    
    i = 0
    while i < 3:
        print("meow")
        i += 1
    

    你可以下载此代码 here 。

  • 2/uppercase0.py

    为了进一步理解 Python 中的循环与迭代,让我们创建一个名为 uppercase.py 的新文件,如下所示:

    # Uppercases string one character at a time
    
    before = input("Before: ")
    print("After:  ", end="")
    for c in before:
        print(c.upper(), end="")
    print()
    

    注意 end= 如何用于向 print 函数传递参数,该函数会续行而不添加换行符。此代码每次传递一个字符串。你可以下载此代码 here 。

  • 2/uppercase1.py

    阅读文档,我们发现 Python 拥有可应用于整个字符串的方法,如下所示:

    # Uppercases string all at once
    
    before = input("Before: ")
    after = before.upper()
    print(f"After:  {after}")
    

    注意 .upper 是如何应用于整个字符串的。你可以下载此代码 here 。

7.1.15. 抽象

  • 1/cat4.py

    正如我们早些时候提到的,你可以使用函数进一步改进代码,并将各种代码抽象为函数。按以下方式修改你之前创建的 meow.py 代码:

    # Abstraction
    
    def main():
        for i in range(3):
            meow()
    
    # Meow once
    def meow():
        print("meow")
    
    main()
    

    请注意, meow 函数抽象了 print 语句。此外,请注意 main 函数出现在文件顶部。在文件底部,调用了 main 函数。按照惯例,你应该在 Python 中创建一个 main 函数。你可以下载此代码 here 。

  • 1/cat5.py

    确实,我们可以按如下方式在函数之间传递变量:

    # Abstraction with parameterization
    
    def main():
        meow(3)
    
    # Meow some number of times
    def meow(n):
        for i in range(n):
            print("meow")
    
    main()
    

    注意 meow 现在接受一个变量 n 。在 main 函数中,你可以调用 meow 并向其传递如 3 之类的值。然后, meow 在 for 循环中使用 n 的值。你可以下载此代码 here 。

  • 阅读上述代码,请注意,作为一名 C 程序员,你能够相当轻松地理解这段代码。尽管某些约定有所不同,但你之前学到的构建块在这门新的编程语言中非常明显。

7.1.16. 截断与浮点不精确

  • 回想一下,在 C 语言中,我们遇到过截断问题,即一个整数除以另一个整数可能会得到不精确的结果。

  • 1/calculator2.py

    你可以通过修改 calculator.py 的代码,观察 Python 如何处理此类除法:

    # Division with integers, demonstration lack of truncation
    
    # Prompt user for x
    x = int(input("x: "))
    
    # Prompt user for y
    y = int(input("y: "))
    
    # Divide x by y
    z = x / y
    print(z)
    

    请注意,执行此代码会产生一个值,但如果你能看到 .333333 之后的更多位数,就会发现我们面临的是 浮点数精度问题 。并未发生截断。你可以下载此代码 here 。

  • 1/calculator3.py

    我们可以通过稍作修改代码来揭示这种不精确性:

    # Floating-point imprecision
    
    # Prompt user for x
    x = int(input("x: "))
    
    # Prompt user for y
    y = int(input("y: "))
    
    # Divide x by y
    z = x / y
    print(f"{z:.50f}")
    

    请注意,此代码揭示了不精确性。Python 仍然面临这个问题,就像 C 一样。你可以下载此代码 here 。

7.1.17. 异常

  • 让我们进一步探讨运行 Python 代码时可能发生的异常。

  • 6/integer/integer1.py

    按如下方式修改 integer.py :

    # Doesn't handle exception
    
    # Prompt user for an integer
    n = int(input("Input: "))
    print("Integer")
    

    请注意,输入错误的数据可能导致错误。你可以下载此代码 here 。

  • 6/integer/integer2.py

    我们可以 try 通过如下修改代码来处理并 捕获 潜在的异常:

    # Handles exception
    
    # Prompt user for an integer
    try:
        n = int(input("Input: "))
        print("Integer.")
    except ValueError:
        print("Not integer.")
    

    请注意,上述代码会反复尝试获取正确类型的数据,并在需要时提供额外的提示。你可以下载此代码 here 。

7.1.18. 马里奥

  • 回想几周前,我们面临的挑战是将三个方块像《超级马里奥》中那样堆叠在一起。

    three vertical blocks

  • 1/mario0.py

    在 Python 中,我们可以按如下方式实现类似的功能:

    # Prints a column of 3 bricks with a loop
    
    for i in range(3):
        print("#")
    

    这将打印一列三块砖。你可以下载此代码 here 。

  • 1/mario1.py

    在 C 中,我们拥有 do-while 循环的优势。然而,在 Python 中,惯例是使用 while 循环,因为 Python 没有 do-while 循环。你可以在一个名为 mario.py 的文件中编写如下代码:

    # Prints a column of n bricks with a loop
    
    from cs50 import get_int
    
    while True:
        n = get_int("Height: ")
        if n > 0:
            break
    
    for i in range(n):
        print("#")
    

    注意 while 循环如何用于获取高度。一旦输入的高度大于零,循环就会终止。你可以下载此代码 here 。

  • 考虑以下图像:

    four horizontal question blocks

  • 1/mario2.py

    在 Python 中,我们可以通过如下方式修改代码来实现:

    # Prints a row of 4 question marks with a loop
    
    for i in range(4):
        print("?", end="")
    print()
    

    请注意,你可以覆盖 print 函数的行为,使其与上一条打印内容保持在同一行。你可以下载此代码 here 。

  • 1/mario3.py

    与之前的迭代类似,我们可以进一步简化该程序:

    # Prints a row of 4 question marks without a loop
    
    print("?" * 4)
    

    注意,我们可以利用 * 来乘以 print 语句,使其重复 4 次。你可以下载此代码 here 。

  • 那么一大块砖头呢?

    three by three block of mario blocks

  • 1/mario4.py

    要实现上述功能,你可以按如下方式修改代码:

    # Prints a 3-by-3 grid of bricks with loops
    
    for i in range(3):
        for j in range(3):
            print("#", end="")
        print()
    

    注意一个 for 循环如何嵌套在另一个循环内部。 print 语句在每行砖块的末尾添加一个新行。你可以下载此代码 here 。

  • 你可以在 Python documentation 中了解更多关于 print 函数的信息

7.1.19. 线性表

  • list 是 Python 中的一种数据结构。

  • list 内部包含内置方法或函数。

  • 2/scores0.py

    例如,考虑以下代码:

    # Averages three numbers using a list
    
    # Scores
    scores = [72, 73, 33]
    
    # Print average
    average = sum(scores) / len(scores)
    print(f"Average: {average}")
    

    请注意,你可以使用内置的 sum 方法来计算平均值。你可以下载此代码 here 。

  • 2/scores1.py

    你甚至可以使用以下语法从用户处获取值:

    # Averages three numbers using a list and a loop
    
    from cs50 import get_int
    
    # Get scores
    scores = []
    for i in range(3):
        score = get_int("Score: ")
        scores.append(score)
    
    # Print average
    average = sum(scores) / len(scores)
    print(f"Average: {average}")
    

    请注意,此代码使用了线性表的内置 append 方法。你可以下载此代码 here 。

  • 你可以在 Python documentation 中了解更多关于线性表的知识

  • 你也可以在 Python documentation 中了解更多关于 len 的知识

7.1.20. 搜索与字典

  • 我们还可以在数据结构中进行搜索。

  • 3/phonebook0.py

    考虑一个名为 phonebook.py 的程序,如下所示:

    # Implements linear search for names using loop
    
    # A list of names
    names = ["Kelly", "David", "John"]
    
    # Ask for name
    name = input("Name: ")
    
    # Search for name
    for n in names:
        if name == n:
            print("Found")
            break
    else:
        print("Not found")
    

    注意这里如何为每个名字实现线性查找。你可以下载此代码 here 。

  • 3/phonebook1.py

    然而,我们无需遍历线性表。在 Python 中,我们可以按如下方式执行线性搜索:

    # Implements linear search for names using `in`
    
    # A list of names
    names = ["Kelly", "David", "John"]
    
    # Ask for name
    name = input("Name: ")
    
    # Search for name
    if name in names:
        print("Found")
    else:
        print("Not found")
    

    注意 in 是如何用于实现线性查找的。你可以下载此代码 here 。

  • 尽管如此,这段代码仍有改进空间。

  • 回想一下,字典 或 dict 是 键 和 值 对的集合。

  • 3/phonebook2.py

    你可以在 Python 中按如下方式实现字典:

    # Implements a phone book as a list of dictionaries, without a variable
    
    from cs50 import get_string
    
    people = [
        {"name": "Kelly", "number": "+1-617-495-1000"},
        {"name": "David", "number": "+1-617-495-1000"},
        {"name": "John", "number": "+1-949-468-2750"},
    ]
    
    # Search for name
    name = get_string("Name: ")
    for person in people:
        if person["name"] == name:
            print(f"Found {person['number']}")
            break
    else:
        print("Not found")
    

    请注意,该字典的实现为每个条目同时包含 name 和 number 。你可以下载此代码 here 。

  • 3/phonebook4.py

    更优的是,严格来说,我们并不需要同时拥有 name 和 number 。我们可以按如下方式简化此代码:

    # Implements a phone book using a dictionary
    
    from cs50 import get_string
    
    people = {
        "Kelly": "+1-617-495-1000",
        "David": "+1-617-495-1000",
        "John": "+1-949-468-2750",
    }
    
    # Search for name
    name = get_string("Name: ")
    if name in people:
        print(f"Number: {people[name]}")
    else:
        print("Not found")
    

    请注意,字典是使用花括号实现的。接着,语句 if name in people 会检查 name 是否存在于 people 字典中。此外,请注意在 print 语句中,我们可以使用 name 的值作为索引来访问 people 字典。非常有用!你可以下载此代码 here 。

  • 3/phonebook3.py

    类似地,可以实现一个由字典组成的线性表:

    # Implements a phone book as a list of dictionaries
    
    people = [
        {"name": "Kelly", "number": "+1-617-495-1000"},
        {"name": "David", "number": "+1-617-495-1000"},
        {"name": "John", "number": "+1-949-468-2750"},
    ]
    
    # Search for name
    name = input("Name: ")
    for person in people:
        if person["name"] == name:
            number = person["number"]
            print(f"Found {number}")
            break
    else:
        print("Not found")
    

    注意 people 是如何实现为由逗号分隔的字典线性表。你可以下载此代码 here 。

  • Python 已尽其所能,利用内置搜索实现 常数时间 。

  • 你可以在 Python documentation 中了解更多关于字典的内容。

7.1.21. 命令行参数

  • 2/greet.py

    与 C 语言一样,你也可以使用命令行参数。考虑以下代码:

    # Prints a command-line argument
    
    from sys import argv
    
    if len(argv) == 2:
        print(f"hello, {argv[1]}")
    else:
        print("hello, world")
    

    请注意, argv[1] 是使用 格式化字符串 打印的,这由 print 语句中存在的 f 所标识。你可以下载此代码 here 。

  • 你可以在 Python documentation 中了解更多关于 sys 库的信息

7.1.22. 退出状态

  • 2/exit.py

    sys 库也提供了内置方法。我们可以使用 sys.exit(i) 以特定的退出码退出程序:

    # Exits with explicit value, importing sys
    
    import sys
    
    if len(sys.argv) != 2:
        print("Missing command-line argument")
        sys.exit(1)
    
    print(f"hello, {sys.argv[1]}")
    sys.exit(0)
    

    请注意,点号表示法用于调用 sys 的内置函数。你可以下载此代码 here 。

7.1.23. CSV 文件

  • Python 也内置了对 CSV 文件的支持。

  • 4/phonebook0.py

    将 phonebook.py 的代码修改如下:

    import csv
    
    file = open("phonebook.csv", "a")
    
    name = input("Name: ")
    number = input("Number: ")
    
    writer = csv.writer(file)
    writer.writerow([name,number])
    
    file.close()
    

    注意 writerow 会替我们在 CSV 文件中添加逗号。你可以下载此代码 here 。

  • 4/phonebook1.py

    虽然 file.close 和 file = open 是 Python 中常用且可用的语法,但此代码可按如下方式改进:

    # Uses `with`
    
    import csv
    
    # Get name and number
    name = input("Name: ")
    number = input("Number: ")
    
    # Open CSV file
    with open("phonebook.csv", "a") as file:
    
        # Print to file
        writer = csv.writer(file)
        writer.writerow([name, number])
    

    请注意,代码在 with 语句下进行了缩进。这会在完成后自动关闭文件。你可以下载此代码 here 。

  • 4/phonebook2.py

    同样地,我们可以在 CSV 文件中按如下方式编写字典:

    import csv
    
    name = input("Name: ")
    number = input("Number: ")
    
    with open("phonebook.csv", "a") as file:
    
        writer = csv.DictWriter(file, fieldnames=["name", "number"])
        writer.writerow({"name": name, "number": number})
    

    注意此代码与我们之前的迭代非常相似,只是将 csv.DictWriter 替换为此处。你可以下载此代码 here 。

7.1.24. 第三方库

  • Python 的优势之一是其庞大的用户群以及数量同样众多的第三方库。

  • 只要已安装 Python ,你就可以通过输入 pip install cs50 在自己的计算机上安装 CS50 库。

  • 在考虑其他库时,David 演示了 cowsay 和 qrcode 的用法。

7.1.25. 总结

在本课中,你学习了如何将先前课程中的编程基础构件在 Python 中实现。此外,你还了解了 Python 如何使代码更加简洁。同时,你学会了如何利用各种 Python 库。最后,你认识到作为一名程序员的技能并不局限于单一编程语言。你已经发现,通过本课程你正在探索一种新的学习方式,这种方式不仅适用于任何编程语言,甚至可能适用于几乎所有的学习领域!具体而言,我们讨论了……

  • Python

  • 变量

  • 条件语句

  • 循环

  • 类型

  • 面向对象编程

  • 截断与浮点精度误差

  • 异常

  • 字典

  • 命令行参数

  • 第三方库

下次再见!

   «  6.1. 第 5 周:数据结构   ::   目录   ::   8.1. 第 7 周:SQL  »

关闭窗口