Week 6 Python

Chào mừng bạn!

  • Trong những tuần trước, bạn đã được làm quen với các khối xây dựng cơ bản của lập trình.

  • Bạn đã học về lập trình bằng ngôn ngữ lập trình bậc thấp tên là C.

  • Hôm nay, chúng ta sẽ làm việc với một ngôn ngữ lập trình bậc cao hơn tên là Python.

  • Khi bạn học ngôn ngữ mới này, bạn sẽ thấy rằng mình có khả năng tự học các ngôn ngữ lập trình mới tốt hơn.

Hello Python!

  • Con người, qua nhiều thập kỷ, đã thấy được cách cải thiện những quyết định thiết kế trước đây trong các ngôn ngữ lập trình cũ.

  • Python là một ngôn ngữ lập trình được xây dựng dựa trên những gì bạn đã học trong C.

  • Ngoài ra, Python còn có quyền truy cập vào một lượng lớn các thư viện do người dùng tạo ra.

  • Khác với C, vốn là một ngôn ngữ biên dịch (compiled language), Python là một ngôn ngữ thông dịch (interpreted language), nơi bạn không cần phải biên dịch chương trình một cách riêng biệt. Thay vào đó, bạn chạy chương trình trong Trình thông dịch Python (Python Interpreter).

Cho đến thời điểm này, mã nguồn trông như thế này:

// A program that says hello to the world

#include <stdio.h>

int main(void)
{
    printf("hello, world\n");
}
  • Hôm nay, bạn sẽ thấy rằng quy trình viết và biên dịch mã đã được đơn giản hóa.

Ví dụ, đoạn mã trên sẽ được thể hiện trong Python như sau:

# A program that says hello to the world

print("hello, world")

Lưu ý rằng dấu chấm phẩy đã biến mất và không cần thư viện nào cả. Bạn có thể chạy chương trình này trong terminal của mình bằng cách gõ python hello.py.

  • Đáng chú ý là Python có thể thực hiện những việc vốn khá phức tạp trong C một cách tương đối đơn giản.

Speller

Để minh họa cho sự đơn giản này, hãy gõ code dictionary.py trong cửa sổ terminal và viết mã như sau:

# 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

Lưu ý rằng có bốn hàm ở trên. Trong hàm check, nếu một word nằm trong words, nó sẽ trả về True. Việc này dễ dàng hơn nhiều so với việc triển khai trong C! Tương tự, trong hàm load, tệp từ điển được mở ra. Với mỗi dòng trong tệp đó, chúng ta thêm dòng đó vào words. Sử dụng rstrip, ký tự dòng mới ở cuối sẽ được loại bỏ khỏi từ được thêm vào. size chỉ đơn giản trả về len hoặc độ dài của words. unload chỉ cần trả về True vì Python tự xử lý việc quản lý bộ nhớ.

  • Đoạn mã trên minh họa lý do tại sao các ngôn ngữ bậc cao tồn tại: Để đơn giản hóa và cho phép bạn viết mã dễ dàng hơn.

  • Tuy nhiên, tốc độ là một sự đánh đổi. Bởi vì C cho phép bạn, lập trình viên, đưa ra các quyết định về quản lý bộ nhớ, nó có thể chạy nhanh hơn Python – tùy thuộc vào mã của bạn. Trong khi C chỉ chạy các dòng mã của bạn, Python chạy tất cả các mã đi kèm bên dưới khi bạn gọi các hàm tích hợp sẵn của Python.

  • Bạn có thể tìm hiểu thêm về các hàm trong tài liệu Python

Filter

Để minh họa rõ hơn sự đơn giản này, hãy tạo một tệp mới bằng cách gõ code blur.py trong cửa sổ terminal của bạn và viết mã như sau:

# 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")

Lưu ý rằng chương trình này nhập (import) các mô-đun ImageImageFilter từ một thư viện tên là PIL. Nó nhận một tệp đầu vào và tạo ra một tệp đầu ra.

Hơn nữa, bạn có thể tạo một tệp mới tên là edges.py như sau:

# Finds edges in an image

from PIL import Image, ImageFilter

# Find edges
before = Image.open("bridge.bmp")
after = before.filter(ImageFilter.FIND_EDGES)
after.save("out.bmp")

Lưu ý rằng đoạn mã này là một sự điều chỉnh nhỏ đối với mã blur của bạn nhưng tạo ra kết quả khác biệt đáng kể.

Python cho phép bạn trừu tượng hóa việc lập trình mà vốn dĩ sẽ phức tạp hơn nhiều trong C và các ngôn ngữ lập trình bậc thấp khác.

Functions

Trong C, bạn có thể đã thấy các hàm như sau:

printf("hello, world\n");

Trong Python, bạn sẽ thấy các hàm như sau:

print("hello, world")

Libraries, Modules, and Packages

  • Cũng giống như C, thư viện CS50 có thể được sử dụng trong Python.

Các hàm sau đây sẽ đặc biệt hữu ích:

  get_float
  get_int
  get_string

Bạn có thể nhập thư viện cs50 như sau:

import cs50

Bạn cũng có tùy chọn chỉ nhập các hàm cụ thể từ thư viện CS50 như sau:

from cs50 import get_float, get_int, get_string

Strings

Trong C, bạn có thể nhớ đoạn mã này:

// 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);
}

Đoạn mã này được chuyển đổi trong Python thành:

# get_string and print, with concatenation

from cs50 import get_string

answer = get_string("What's your name? ")
print("hello, " + answer)

Bạn có thể viết đoạn mã này bằng cách thực thi code hello.py trong cửa sổ terminal. Sau đó, bạn có thể thực thi đoạn mã này bằng cách chạy python hello.py. Lưu ý cách dấu + nối chuỗi "hello, "answer.

Tương tự, việc này có thể được thực hiện mà không cần nối chuỗi:

# get_string and print, without concatenation

from cs50 import get_string

answer = get_string("What's your name? ")
print("hello,", answer)

Lưu ý rằng câu lệnh print tự động tạo một khoảng trắng giữa câu lệnh helloanswer.

Tương tự, bạn có thể triển khai đoạn mã trên như sau:

# get_string and print, with format strings

from cs50 import get_string

answer  = get_string("What's your name? ")
print(f"hello, {answer}")

Lưu ý cách các dấu ngoặc nhọn cho phép hàm print nội suy (interpolate) biến answer để answer xuất hiện bên trong. Chữ f là bắt buộc để bao gồm answer và định dạng đúng cách.

Positional Parameters and Named Parameters

  • Các hàm trong C như fread, fwrite, và printf sử dụng các đối số theo vị trí (positional arguments), nơi bạn cung cấp các đối số với dấu phẩy làm dấu phân cách. Bạn, lập trình viên, phải nhớ đối số nào nằm ở vị trí nào. Chúng được gọi là đối số theo vị trí.

  • Trong Python, tham số có tên (named parameters) cho phép bạn cung cấp các đối số mà không cần quan tâm đến vị trí.

  • Bạn có thể tìm hiểu thêm về các tham số của hàm print trong tài liệu.

Truy cập tài liệu đó, bạn có thể thấy như sau:

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

Lưu ý rằng nhiều đối tượng khác nhau có thể được cung cấp cho print. Một dấu phân cách là một khoảng trắng duy nhất được cung cấp và sẽ hiển thị khi có nhiều hơn một đối tượng được đưa vào print. Tương tự, một dòng mới được cung cấp ở cuối câu lệnh print.

Variables

  • Khai báo biến cũng được đơn giản hóa. Trong C, bạn có thể có int counter = 0;. Trong Python, dòng tương tự này sẽ là counter = 0. Bạn không cần phải khai báo kiểu của biến.

  • Python ưu tiên sử dụng counter += 1 để tăng thêm một, mất đi khả năng gõ counter++ như trong C.

Types

  • Các kiểu dữ liệu trong Python không cần phải được khai báo rõ ràng. Ví dụ, bạn đã thấy answer ở trên là một chuỗi, nhưng chúng ta không cần phải báo cho trình thông dịch biết trường hợp này: Nó tự biết.

Trong Python, các kiểu thường được sử dụng bao gồm:

  bool
  float
  int
  str

Lưu ý rằng longdouble đã biến mất. Python sẽ xử lý kiểu dữ liệu nào nên được sử dụng cho các số lớn hơn và nhỏ hơn.

Một số kiểu dữ liệu khác trong Python bao gồm:

range   dãy số
list    dãy các giá trị có thể thay đổi (mutable)
tuple   dãy các giá trị không thể thay đổi (immutable)
dict    tập hợp các cặp khóa-giá trị (key-value)
set     tập hợp các giá trị duy nhất
  • Mỗi kiểu dữ liệu này đều có thể được triển khai trong C, nhưng trong Python, chúng có thể được triển khai đơn giản hơn.

Calculator

Bạn có thể nhớ calculator.c từ phần trước của khóa họ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);
}

Chúng ta có thể triển khai một máy tính đơn giản giống như đã làm trong C. Gõ code calculator.py vào cửa sổ terminal và viết mã như sau:

# 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)

Lưu ý cách thư viện CS50 được nhập vào. Sau đó, xy được thu thập từ người dùng. Cuối cùng, kết quả được in ra. Lưu ý rằng hàm main vốn thường thấy trong một chương trình C đã hoàn toàn biến mất! Mặc dù người ta có thể sử dụng hàm main, nhưng nó không bắt buộc.

Người ta có thể gỡ bỏ “bánh xe tập đi” của thư viện CS50. Hãy sửa đổi mã của bạn như sau:

# Addition with int [using input]

# Prompt user for x
x = input("x: ")

# Prompt user for y
y = input("y: ")

# Perform addition
print(x + y)

Lưu ý rằng việc thực thi đoạn mã trên dẫn đến hành vi chương trình lạ lùng. Tại sao lại như vậy?

Có thể bạn đã đoán rằng trình thông dịch hiểu xy là các chuỗi. Bạn có thể sửa mã của mình bằng cách sử dụng hàm int như sau:

# 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)

Lưu ý cách đầu vào cho xy được chuyển qua hàm int, hàm này sẽ chuyển đổi nó thành một số nguyên. Nếu không chuyển đổi xy thành số nguyên, các ký tự sẽ bị nối lại với nhau.

Conditionals

Trong C, bạn có thể nhớ một chương trình như thế này:

// 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");
    }
}

Trong Python, nó sẽ xuất hiện như sau:

# 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")

Lưu ý rằng không còn dấu ngoặc nhọn nữa. Thay vào đó, việc thụt lề được sử dụng. Thứ hai, dấu hai chấm được sử dụng trong câu lệnh if. Hơn nữa, elif thay thế cho else if. Dấu ngoặc đơn cũng không còn bắt buộc trong các câu lệnh ifelif.

Xem xét thêm về các phép so sánh, hãy cân nhắc đoạn mã sau trong 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 if (c == 'N' || c == 'n')
    {
        printf("Not agreed.\n");
    }
}

Đoạn mã trên có thể được triển khai như sau:

# 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.")
elif s == "N" or s == "n":
    print("Not agreed.")

Lưu ý rằng hai thanh dọc được sử dụng trong C được thay thế bằng or. Thực vậy, mọi người thường thích Python vì nó dễ đọc hơn đối với con người. Ngoài ra, hãy lưu ý rằng char không tồn tại trong Python. Thay vào đó, str được sử dụng.

Một cách tiếp cận khác cho cùng đoạn mã này có thể là sử dụng danh sách (lists) như sau:

# 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.")
elif s in ["n", "no"]:
    print("Not agreed.")

Lưu ý cách chúng ta có thể biểu diễn nhiều từ khóa như yyes trong một list.

Object-Oriented Programming

  • Có thể có một số kiểu giá trị không chỉ có các đặc tính hoặc thuộc tính bên trong chúng mà còn có cả các hàm nữa. Trong Python, những giá trị này được gọi là đối tượng (objects).

  • Trong C, chúng ta có thể tạo một struct nơi bạn có thể liên kết nhiều biến bên trong một kiểu dữ liệu tự tạo duy nhất. Trong Python, chúng ta có thể làm điều này và cũng bao gồm các hàm trong một kiểu dữ liệu tự tạo. Khi một hàm thuộc về một đối tượng cụ thể, nó được gọi là một phương thức (method).

Ví dụ, str trong Python có các phương thức tích hợp sẵn. Do đó, bạn có thể sửa đổi mã của mình như sau:

# Logical operators, using lists

# Prompt user to agree
s = input("Do you agree? ").lower()

# Check whether agreed
if s in ["y", "yes"]:
    print("Agreed.")
elif s in ["n", "no"]:
    print("Not agreed.")

Lưu ý cách giá trị cũ của s bị ghi đè bởi kết quả của s.lower(), một phương thức tích hợp sẵn của str.

Tương tự, bạn có thể nhớ cách chúng ta sao chép một chuỗi trong 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;
}

Lưu ý số lượng dòng mã.

Chúng ta có thể triển khai đoạn mã trên trong Python như sau:

# 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}")

Lưu ý chương trình này ngắn hơn bao nhiêu so với bản đối chiếu của nó trong C.

  • Trong lớp học này, chúng ta sẽ chỉ mới bắt đầu tìm hiểu bề nổi của Python. Do đó, tài liệu Python sẽ có tầm quan trọng đặc biệt khi bạn tiếp tục học.

  • Bạn có thể tìm hiểu thêm về các phương thức chuỗi trong tài liệu Python

Loops

Vòng lặp trong Python rất giống với C. Bạn có thể nhớ đoạn mã sau trong C:

// Demonstrates for loop

#include <stdio.h>

int main(void)
{
    for (int i = 0; i < 3; i++)
    {
        printf("meow\n");
    }
}

Vòng lặp for có thể được triển khai trong Python như sau:

# Better design

for i in range(3):
    print("meow")

Lưu ý rằng i không bao giờ được sử dụng một cách rõ ràng. Tuy nhiên, Python sẽ tự tăng giá trị của i.

Hơn nữa, một vòng lặp while có thể được triển khai như sau:

# Demonstrates while loop

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

Để hiểu thêm về vòng lặp và phép lặp trong Python, hãy tạo một tệp mới tên là uppercase.py như sau:

# Uppercases string one character at a time

before = input("Before: ")
print("After:  ", end="")
for c in before:
    print(c.upper(), end="")
print()

Lưu ý cách end= được sử dụng để truyền một tham số vào hàm print giúp tiếp tục dòng mà không có kết thúc dòng. Đoạn mã này truyền từng chuỗi một tại một thời điểm.

Đọc tài liệu, chúng ta khám phá ra rằng Python có các phương thức có thể được triển khai trên toàn bộ chuỗi như sau:

# Uppercases string all at once

before = input("Before: ")
after = before.upper()
print(f"After:  {after}")

Lưu ý cách .upper được áp dụng cho toàn bộ chuỗi.

Abstraction

Như chúng ta đã gợi ý ở phần trước hôm nay, bạn có thể cải thiện thêm mã của chúng ta bằng cách sử dụng các hàm và trừu tượng hóa các phần mã khác nhau vào các hàm. Hãy sửa đổi mã meow.py bạn đã tạo trước đó như sau:

# Abstraction

def main():
    for i in range(3):
        meow()

# Meow once
def meow():
    print("meow")

main()

Lưu ý rằng hàm meow trừu tượng hóa câu lệnh print. Hơn nữa, hãy lưu ý rằng hàm main xuất hiện ở đầu tệp. Ở cuối tệp, hàm main được gọi. Theo quy ước, bạn được kỳ vọng sẽ tạo một hàm main trong Python.

Thực vậy, chúng ta có thể truyền các biến giữa các hàm của mình như sau:

# Abstraction with parameterization

def main():
    meow(3)

# Meow some number of times
def meow(n):
    for i in range(n):
        print("meow")

main()

Lưu ý cách meow hiện nhận một biến n. Trong hàm main, bạn có thể gọi meow và truyền một giá trị như 3 cho nó. Sau đó, meow sử dụng giá trị của n trong vòng lặp for.

Đọc đoạn mã trên, hãy lưu ý cách bạn, với tư cách là một lập trình viên C, có thể hiểu đoạn mã trên khá dễ dàng. Mặc dù một số quy ước có khác biệt, nhưng những khối xây dựng bạn đã học trước đó rất rõ ràng trong ngôn ngữ lập trình mới này.

Truncation and Floating Point Imprecision

  • Nhớ lại rằng trong C, chúng ta đã gặp hiện tượng cắt cụt (truncation), nơi một số nguyên chia cho một số nguyên khác có thể dẫn đến kết quả không chính xác.

Bạn có thể thấy cách Python xử lý phép chia như vậy bằng cách sửa đổi mã của mình cho calculator.py:

# 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)

Lưu ý rằng việc thực thi đoạn mã này cho ra một giá trị, nhưng nếu bạn thấy nhiều chữ số hơn sau .333333, bạn sẽ thấy rằng chúng ta đang đối mặt với sự không chính xác của số dấu phẩy động (floating-point imprecision). Hiện tượng cắt cụt không xảy ra.

Chúng ta có thể để lộ sự không chính xác này bằng cách sửa đổi nhẹ mã của mình:

# 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}")

Lưu ý rằng đoạn mã này để lộ sự không chính xác. Python vẫn đối mặt với vấn đề này, giống như C.

Exceptions

  • Hãy cùng tìm hiểu thêm về các ngoại lệ (exceptions) có thể xảy ra khi chúng ta chạy mã Python.

Sửa đổi calculator.py như sau:

# Doesn't handle exception

# Prompt user for an integer
n = int(input("Input: "))
print("Integer")

Lưu ý rằng việc nhập dữ liệu sai có thể dẫn đến lỗi.

Chúng ta có thể try để xử lý và bắt (catch) các ngoại lệ tiềm ẩn bằng cách sửa đổi mã của mình như sau:

# Handles exception

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

Lưu ý rằng đoạn mã trên liên tục thử lấy đúng kiểu dữ liệu, cung cấp thêm lời nhắc khi cần thiết.

Mario

Hãy nhớ lại thử thách xây dựng ba khối chồng lên nhau cách đây vài tuần, giống như trong Mario.

Trong Python, chúng ta có thể triển khai thứ gì đó tương tự như sau:

# Prints a column of 3 bricks with a loop

for i in range(3):
    print("#")

Đoạn mã này in một cột gồm ba viên gạch.

Trong C, chúng ta có lợi thế của vòng lặp do-while. Tuy nhiên, trong Python, theo quy ước là sử dụng vòng lặp while, vì Python không có vòng lặp do-while. Bạn có thể viết mã như sau trong một tệp tên là 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("#")

Lưu ý cách vòng lặp while được sử dụng để lấy chiều cao. Khi một chiều cao lớn hơn không được nhập vào, vòng lặp sẽ kết thúc (break).

Hãy xem xét hình ảnh sau:

Trong Python, chúng ta có thể triển khai bằng cách sửa đổi mã của mình như sau:

# Prints a row of 4 question marks with a loop

for i in range(4):
    print("?", end="")
print()

Lưu ý rằng bạn có thể ghi đè hành vi của hàm print để ở lại trên cùng một dòng với lần in trước đó.

Tương tự như tinh thần của các lần lặp trước, chúng ta có thể đơn giản hóa thêm chương trình này:

# Prints a row of 4 question marks without a loop

print("?" * 4)

Lưu ý rằng chúng ta có thể sử dụng * để nhân câu lệnh in nhằm lặp lại 4 lần.

Còn về một khối gạch lớn thì sao?

Để triển khai điều trên, bạn có thể sửa đổi mã của mình như sau:

# Prints a 3-by-3 grid of bricks with loops

for i in range(3):
    for j in range(3):
        print("#", end="")
    print()

Lưu ý cách một vòng lặp for nằm bên trong một vòng lặp khác. Câu lệnh print thêm một dòng mới ở cuối mỗi hàng gạch.

Bạn có thể tìm hiểu thêm về hàm print trong tài liệu Python

Lists

list (danh sách) là một cấu trúc dữ liệu trong Python.

list có các phương thức hoặc hàm tích hợp sẵn bên trong chúng.

Ví dụ, hãy xem xét đoạn mã sau:

# Averages three numbers using a list

# Scores
scores = [72, 73, 33]

# Print average
average = sum(scores) / len(scores)
print(f"Average: {average}")

Lưu ý rằng bạn có thể sử dụng phương thức sum tích hợp sẵn để tính trung bình cộng.

Bạn thậm chí có thể sử dụng cú pháp sau để lấy các giá trị từ người dùng:

# 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}")

Lưu ý rằng đoạn mã này sử dụng phương thức append tích hợp sẵn cho danh sách.

Searching and Dictionaries

  • Chúng ta cũng có thể tìm kiếm bên trong một cấu trúc dữ liệu.

Hãy xem xét một chương trình tên là phonebook.py như sau:

# Implements linear search for names using loop

# A list of names
names = ["Yuliia", "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")

Lưu ý cách đoạn mã này triển khai tìm kiếm tuyến tính (linear search) cho từng tên.

Tuy nhiên, chúng ta không cần phải lặp qua một danh sách. Trong Python, chúng ta có thể thực thi tìm kiếm tuyến tính như sau:

# Implements linear search for names using `in`

# A list of names
names = ["Yuliia", "David", "John"]

# Ask for name
name = input("Name: ")

# Search for name
if name in names:
    print("Found")
else:
    print("Not found")

Lưu ý cách in được sử dụng để triển khai tìm kiếm tuyến tính.

  • Tuy nhiên, đoạn mã này vẫn có thể được cải thiện.

  • Hãy nhớ lại rằng một từ điển (dictionary) hay dict là một tập hợp các cặp khóa (key) và giá trị (value).

Bạn có thể triển khai một từ điển trong Python như sau:

# Implements a phone book as a list of dictionaries, without a variable

from cs50 import get_string

people = [
    {"name": "Yuliia", "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")

Lưu ý rằng từ điển được triển khai có cả namenumber cho mỗi mục nhập.

Thậm chí tốt hơn, nói một cách chính xác, chúng ta không cần cả namenumber. Chúng ta có thể đơn giản hóa đoạn mã này như sau:

# Implements a phone book using a dictionary

from cs50 import get_string

people = {
    "Yuliia": "+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")

Lưu ý rằng từ điển được triển khai bằng cách sử dụng dấu ngoặc nhọn. Sau đó, câu lệnh if name in people tìm kiếm để xem liệu name có nằm trong từ điển people hay không. Hơn nữa, hãy lưu ý cách trong câu lệnh print, chúng ta có thể lập chỉ mục (index) vào từ điển people bằng giá trị của name. Rất hữu ích!

  • Python đã cố gắng hết sức để đạt được thời gian hằng số (constant time) bằng cách sử dụng các tìm kiếm tích hợp sẵn của họ.

  • Bạn có thể tìm hiểu thêm về từ điển trong tài liệu Python

Command-Line Arguments

Giống như C, bạn cũng có thể sử dụng các đối số dòng lệnh. Hãy xem xét đoạn mã sau:

# Prints a command-line argument

from sys import argv

if len(argv) == 2:
    print(f"hello, {argv[1]}")
else:
    print("hello, world")

Lưu ý rằng argv[1] được in ra bằng một chuỗi định dạng (formatted string), được đánh dấu bằng chữ f có trong câu lệnh print.

Bạn có thể tìm hiểu thêm về thư viện sys trong tài liệu Python

Exit Status

Thư viện sys cũng có các phương thức tích hợp sẵn. Chúng ta có thể sử dụng sys.exit(i) để thoát chương trình với một mã thoát (exit code) cụ thể:

# 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)

Lưu ý rằng ký hiệu dấu chấm (dot-notation) được sử dụng để sử dụng các hàm tích hợp sẵn của sys.

CSV Files

  • Python cũng có hỗ trợ tích hợp sẵn cho các tệp CSV.

Sửa đổi mã cho phonebook.py của bạn như sau:

import csv

file = open("phonebook.csv", "a")

name = input("Name: ")
number = input("Number: ")

writer = csv.writer(file)
writer.writerow([name,number])

file.close()

Lưu ý writerow thêm các dấu phẩy trong tệp CSV cho chúng ta.

Mặc dù file.closefile = open là cú pháp thường được sử dụng và có sẵn trong Python, đoạn mã này có thể được cải thiện như sau:

import csv

name = input("Name: ")
number = input("Number: ")

with open("phonebook.csv", "a") as file:

    writer = csv.writer(file)
    writer.writerow([name,number])

Lưu ý rằng mã được thụt lề dưới câu lệnh with. Việc này sẽ tự động đóng tệp khi hoàn tất.

Tương tự, chúng ta có thể viết một từ điển như sau vào trong tệp 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})

Lưu ý đoạn mã này khá giống với lần lặp trước của chúng ta nhưng thay vào đó là sử dụng csv.DictWriter.

Third-Party Libraries

  • Một trong những ưu điểm của Python là lượng người dùng khổng lồ và số lượng thư viện bên thứ ba cũng lớn tương đương.

  • Bạn có thể cài đặt Thư viện CS50 trên máy tính của mình bằng cách gõ pip install cs50, với điều kiện là bạn đã cài đặt Python.

  • Cân nhắc các thư viện khác, David đã trình diễn việc sử dụng cowsayqrcode.

Summing Up

Trong bài học này, bạn đã học cách triển khai các khối xây dựng của lập trình từ các bài học trước trong Python. Hơn nữa, bạn đã học về cách Python cho phép mã đơn giản hơn. Ngoài ra, bạn đã học cách sử dụng các thư viện Python khác nhau. Cuối cùng, bạn đã học được rằng kỹ năng lập trình viên của bạn không bị giới hạn trong một ngôn ngữ lập trình duy nhất. Bạn đã thấy cách mình đang khám phá một cách học mới thông qua khóa học này, điều có thể giúp ích cho bạn trong bất kỳ ngôn ngữ lập trình nào – và có lẽ, trong hầu hết mọi con đường học tập! Cụ thể, chúng ta đã thảo luận về…

  • Python

  • Biến (Variables)

  • Câu lệnh điều kiện (Conditionals)

  • Vòng lặp (Loops)

  • Các kiểu dữ liệu (Types)

  • Lập trình hướng đối tượng (Object-Oriented programming)

  • Cắt cụt và sự không chính xác của số dấu phẩy động (Truncation and floating point imprecision)

  • Ngoại lệ (Exceptions)

  • Từ điển (Dictionaries)

  • Đối số dòng lệnh (Command-line arguments)

  • Thư viện bên thứ ba (Third-Party libraries)

Hẹn gặp lại bạn lần sau!