Th4 192020
 

Chúng ta lại tiếp tục phần nói đến hàm trong python.
Quản lý các ngoại lệ (Exception handling)
Ngay bây giờ, gặp lỗi hoặc ngoại lệ, trong chương trình Python của bạn có nghĩa là toàn bộ chương trình sẽ bị sập. Bạn không muốn điều này xảy ra trong các chương trình thực tế.
Thay vào đó, bạn muốn chương trình phát hiện lỗi, xử lý chúng và rồi tiếp tục chạy.
Ví dụ, hãy xem xét chương trình sau, có lỗi chia cho số 0. Mở cửa sổ soạn thảo văn bản và nhập mã sau đây, lưu nó dưới dạng zeroDivide.py:

def spam(divideBy):
    return 42 / divideBy
print(spam(2))
print(spam(12))
print(spam(0))
print(spam(1))

Chúng tôi đã xác định một hàm gọi là spam, cung cấp cho nó một tham số và sau đó in giá trị của hàm đó với các tham số khác nhau để xem điều gì sẽ xảy ra.
Đây là đầu ra bạn nhận được khi chạy mã trước đó:

Traceback (most recent call last):
	File "C:/zeroDivide.py", line 6, in <module>
		print(spam(0))
	File "C:/zeroDivide.py", line 2, in spam
		return 42 / divideBy
ZeroDivisionError: division by zero

Một thông báo ZeroDivisionError xảy ra bất cứ khi nào bạn cố chia số cho 0. Từ số dòng được đưa ra trong thông báo lỗi, bạn biết rằng câu lệnh return trong spam() đang gây ra lỗi.
Lỗi có thể được xử lý với try và except trừ báo cáo. Mã mà có khả năng có một lỗi được đặt trong một mệnh đề try. Việc thực hiện chương trình di chuyển đến điểm bắt đầu của mệnh đề sau trừ khi xảy ra lỗi.
Bạn có thể đặt mã chia cho 0 trước đó trong mệnh đề try và có mệnh đề except chứa mã để xử lý những gì xảy ra khi lỗi này xảy ra.

def spam(divideBy):
	try:
		return 42 / divideBy
	except ZeroDivisionError:
		print('Error: Invalid argument.')
print(spam(2))
print(spam(12))
print(spam(0))
print(spam(1))

Khi mã trong mệnh đề try gây ra lỗi, việc thực thi chương trình ngay lập tức chuyển sang mã trong mệnh đề except. Sau khi chạy mã đó, việc thực thi tiếp tục như bình thường. Đầu ra của chương trình trước như sau:

21.0
3.5
Error: Invalid argument.
None
42.0

Lưu ý rằng bất kỳ lỗi nào xảy ra trong các lệnh gọi hàm trong khối try cũng sẽ bị bắt. Hãy xem xét chương trình sau, thay vào đó có các cuộc gọi spam() trong khối try:

def spam(divideBy):
	return 42 / divideBy
try:
	print(spam(2))
	print(spam(12))
	print(spam(0))
	print(spam(1))
except ZeroDivisionError:
	print('Error: Invalid argument.')

Khi chương trình này được chạy, đầu ra trông như thế này:

21.0
3.5
Error: Invalid argument.

Lý do print(spam (1)) không bao giờ được thực thi là vì một lần thực thi nhảy đến mã trong mệnh đề except, nó không trở về mệnh đề try. Thay vào đó, nó chỉ tiếp tục di chuyển xuống chương trình như bình thường.
Một chương trình ngắn: Zigzag
Cho phép sử dụng các khái niệm lập trình mà bạn đã học từ trước đến nay để tạo ra một chương trình hoạt hình nhỏ. Chương trình này sẽ tạo ra một mô hình ngoằn ngoèo, ngoằn ngoèo cho đến khi người dùng dừng nó bằng cách nhấn ctrl-C. Khi bạn chạy chương trình này, đầu ra sẽ trông giống như thế này:

>     ********
>    ********
>   ********
>  ********
> ********
>  ********
>   ********
>    ********
>     ********

Nhập đoạn code sau vào trình soạn thảo và lưu nó dưới dạng zigzag.py.

import time, sys
indent = 0 # How many spaces to indent.
indentIncreasing = True # Whether the indentation is increasing or not.
try:
	while True: # The main program loop.
		print(' ' * indent, end='')
		print('********')
		time.sleep(0.1) # Pause for 1/10 of a second.
		if indentIncreasing:
			# Increase the number of spaces:
			indent = indent + 1
		    if indent == 20:
			    # Change direction:
			    indentIncreasing = False
		else:
			# Decrease the number of spaces:
			indent = indent - 1
		    if indent == 0:
			    # Change direction:
			    indentIncreasing = True
			
except KeyboardInterrupt:
    sys.exit()

Chúng ta sẽ phân tích từng phần của chương trình này, đầu tiên

import time, sys
indent = 0 # How many spaces to indent.
indentIncreasing = True # Whether the indentation is increasing or not.

Đầu tiên, chúng tôi sẽ nhập các mô-đun time và sys. Chương trình của chúng tôi sử dụng hai biến: biến indent theo dõi xem có bao nhiêu khoảng cách thụt đầu dòng trước dải tám dấu sao và indentIncreasing có chứa giá trị Boolean để xác định xem lượng thụt đầu tăng hay giảm.

try:
	while True: # The main program loop.
		print(' ' * indent, end='')
		print('********')
		time.sleep(0.1) # Pause for 1/10 of a second.

Tiếp theo, chúng tôi đặt phần còn lại của chương trình trong một lệnh try. Khi người dùng nhấn Ctrl-C trong khi chương trình Python đang chạy, Python sẽ tạo ra exception KeyboardInterrupt. Nếu không có câu lệnh ngoại trừ try để bắt exception này, chương trình gặp sự cố với thông báo lỗi xấu. Tuy nhiên, đối với chương trình của chúng tôi, chúng tôi muốn nó xử lý sạch ngoại lệ KeyboardInterrupt bằng cách gọi sys.exit(). (Mã cho điều này nằm trong câu lệnh except ở cuối chương trình.)
Vòng lặp while: vòng lặp vô hạn sẽ lặp lại các hướng dẫn trong chương trình của chúng tôi mãi mãi. Điều này liên quan đến việc sử dụng ‘’ * thụt lề để in đúng số lượng không gian thụt lề. Chúng tôi không muốn tự động in một dòng mới sau các khoảng trắng này, vì vậy chúng tôi cũng chuyển end = ‘’ cho lệnh print() đầu tiên. Một cuộc gọi print() thứ hai ra các dấu hoa thị. Hàm time.sleep() chưa được bao phủ, nhưng đủ để nói rằng nó giới thiệu tạm dừng một phần mười giây trong chương trình của chúng ta tại thời điểm này.

if indentIncreasing:
			# Increase the number of spaces:
			indent = indent + 1
			if indent == 20:
				# Change direction:
				indentIncreasing = False

Tiếp theo, chúng tôi muốn điều chỉnh lượng thụt đầu dòng cho lần tiếp theo chúng tôi in dấu sao. Nếu indentIncreasing là True, thì chúng tôi muốn thêm một vào indent. Nhưng một khi thụt lề đạt 20, chúng tôi muốn indent giảm.

else:
			# Decrease the number of spaces:
			indent = indent - 1
			if indent == 0:
				# Change direction:
				indentIncreasing = True

Trong khi đó, nếu indentIncreasing là Sai, chúng tôi muốn trừ một từ indent. Khi indent đạt 0, chúng tôi muốn indent tăng một lần nữa. Dù bằng cách nào, việc thực hiện chương trình sẽ quay trở lại điểm bắt đầu của vòng lặp chương trình chính để in lại các dấu sao.

except KeyboardInterrupt:
	sys.exit()

Nếu người dùng nhấn Ctrl-C tại bất kỳ điểm nào mà chương trình thực thi nằm trong khối try, except KeyboardInterrrupt được đưa ra và xử lý bằng cách này ngoại trừ câu lệnh. Việc thực thi chương trình di chuyển bên trong khối except, chạy sys.exit() và thoát khỏi chương trình. Bằng cách này, mặc dù vòng lặp chương trình chính là vòng lặp vô hạn, người dùng có cách tắt chương trình.
Tổng kết phần hàm trong python
Các hàm là cách chính để sắp xếp mã của bạn thành các nhóm logic. Do các biến trong các hàm tồn tại trong phạm vi cục bộ của riêng chúng, mã trong một hàm không thể ảnh hưởng trực tiếp đến các giá trị của các biến trong các hàm khác. Điều này giới hạn mã nào có thể thay đổi giá trị của các biến của bạn, điều này có thể hữu ích khi gỡ lỗi mã của bạn. Các hàm là một công cụ tuyệt vời để giúp bạn tổ chức mã của mình. Bạn có thể nghĩ về chúng như các hộp đen: chúng có đầu vào ở dạng tham số và đầu ra ở dạng giá trị trả về và mã trong chúng không ảnh hưởng đến các biến trong các hàm khác.
Trong các chương trước, một lỗi duy nhất có thể khiến chương trình của bạn bị sập.
Trong chương này, bạn đã tìm hiểu về try và except các câu lệnh, có thể chạy mã khi phát hiện lỗi. Điều này có thể làm cho chương trình của bạn trở nên linh hoạt hơn đối với các trường hợp lỗi phổ biến.
Câu hỏi thực hành

  1. Tại sao các hàm có lợi trong các chương trình của bạn?
  2. Khi nào mã trong hàm thực thi: khi hàm được định nghĩa hoặc khi hàm được gọi?
  3. Lệnh để tạo ra hàm là gì?
  4. Điều gì là khác nhau giữa một hàm và lời gọi một hàm?
  5. Có bao nhiêu phạm vi toàn cục trong chương trình python? Và bao nhiêu phạm vi cục bộ?
  6. Điều gì xảy ra với biến cục bộ trong hàm python khi hàm được gọi?
  7. Điều gì là return giá trị? Có thể giá trị return là một phần của biểu thức?
  8. Nếu một hàm không có câu lệnh return. Giá trị nào được trả về khi hàm được gọi.
  9. Làm thế nào bạn có thể buộc một biến trong một hàm tham chiếu đến biến toàn cục?
  10. Dạng dữ liệu của None là gì?
  11. Điều gì sẽ được thực hiện khi chúng ta dùng lệnh import areallyourpetsnamederic?
  12. Nếu bạn có một hàm bacon() trong module spam bạn sẽ gọi hàm bacon() như thế nào sau khi đã import spam?
  13. Làm thế nào bạn có thể ngăn chương trình bị sập khi nó gặp lỗi?
  14. Điều gì là mệnh đề try? Điều gì là mệnh đề except?

Bài tập thực hành
Để thực hành, viết chương trình theo những nhiệm vụ sau.
Dãy Collatz
Viết hàm có tên collatz () có một tham số có tên là number. Nếu number là số chẵn thì collatz () sẽ in number // 2 và trả về giá trị này. Nếu number là số lẻ, thì collatz () sẽ in và trả về 3 * number + 1. Sau đó viết chương trình cho phép người dùng nhập số nguyên và tiếp tục gọi collatz () trên số đó cho đến khi hàm trả về giá trị 1.
(Thật đáng ngạc nhiên, chuỗi này thực sự hoạt động cho bất kỳ số nguyên nào sớm hay muộn, sử dụng chuỗi này, bạn sẽ đến 1! Ngay cả các nhà toán học cũng không chắc tại sao. Chương trình của bạn đang khám phá cái gọi là chuỗi Collatz, đôi khi được gọi là đơn giản nhất vấn đề toán học không thể.)
Nhớ chuyển đổi giá trị trả về từ input () thành một số nguyên với hàm int (); nếu không, nó sẽ là một giá trị chuỗi.
Gợi ý: Số nguyên là số chẵn nếu số% 2 == 0 và số lẻ lẻ nếu số% 2 == 1.
Đầu ra của chương trình này có thể trông giống như thế này:

Enter number:
3
10
5
16
8
4
2
1

Kiểm tra đầu vào
Thêm try và except các câu lệnh cho dự án trước đó để phát hiện xem người dùng có nhập chuỗi không liên kết hay không. Thông thường, hàm int () sẽ đưa ra lỗi ValueError nếu nó được truyền qua một chuỗi không phân phối, như trong int (‘puppy’). Trong mệnh đề except, in một thông báo cho người dùng nói rằng họ phải nhập một số nguyên.