Mục lục bài viết:
- Bắt đầu với các hàm Python
- Hiểu câu lệnh trả về Python
- Tuyên bố trả lại rõ ràng
- Báo cáo trả lại ngầm định
- Trả lại và in
- Trả lại nhiều giá trị
- Sử dụng câu lệnh trả về
Python: Các phương pháp hay nhất
- Trả lại Không rõ ràng
- Ghi nhớ giá trị trả lại
- Tránh các biểu thức phức tạp
- Trả lại giá trị so với sửa đổi toàn cầu
- Sử dụng trả lại có điều kiện
- Trả lại Đúng hay Sai
- Vòng lặp ngắn
- Nhận biết mã chết
- Trả lại nhiều đối tượng được đặt tên
- Chức năng quay lại: Đóng
cửa
- Chức năng Nhận và Trả hàng: Trang trí
- Trả lại các đối tượng do người dùng xác định: Mẫu ban đầu
- Sử dụng trả lại trong lần thử… cuối cùng là Khối
- Sử dụng return trong các Hàm của Trình tạo
- Phần kết luận
Câu return
lệnh Python là một thành phần chính của các hàm và phương thức . Bạn có thể sử dụng return
câu lệnh để làm cho các hàm của bạn gửi các đối tượng Python trở lại mã người gọi. Các đối tượng này được gọi là giá trị trả về của hàm . Bạn có thể sử dụng chúng để thực hiện tính toán thêm trong các chương
trình của mình.
Sử dụng return
câu lệnh một cách hiệu quả là một kỹ năng cốt lõi nếu bạn muốn viết mã các hàm tùy chỉnh theo kiểu Pythonic và mạnh mẽ.
Trong hướng dẫn này, bạn sẽ học:
- Cách sử dụng câu lệnh Python
return
trong các hàm của bạn - Cách trả về một hoặc nhiều giá trị từ các hàm của bạn
- Các phương pháp
hay nhất cần tuân thủ khi sử dụng
return
câu lệnh
Với kiến thức này, bạn sẽ có thể viết các hàm dễ đọc, dễ bảo trì và ngắn gọn hơn bằng Python. Nếu bạn hoàn toàn mới với các hàm Python, thì bạn có thể xem Định nghĩa hàm Python của riêng bạn trước khi đi
sâu vào hướng dẫn này.
Phần thưởng miễn phí: 5 Suy nghĩ khi Làm chủ Python , một khóa học miễn phí dành cho các nhà phát triển Python, cho bạn thấy lộ trình và tư duy mà bạn sẽ cần để nâng các kỹ năng Python của mình lên một cấp độ tiếp theo.
Bắt đầu với các hàm Python
Hầu hết các ngôn ngữ lập trình cho phép bạn gán tên cho một khối mã thực hiện một phép tính cụ thể. Các khối mã
được đặt tên này có thể được sử dụng lại nhanh chóng vì bạn có thể sử dụng tên của chúng để gọi chúng từ các vị trí khác nhau trong mã của mình.
Các lập trình viên gọi các khối mã được đặt tên này là các chương trình con , quy trình , thủ tục hoặc hàm tùy thuộc vào ngôn ngữ mà họ sử dụng. Trong một số ngôn ngữ, có sự khác biệt rõ ràng giữa một quy trình hoặc thủ tục và một
hàm.
Đôi khi sự khác biệt đó quá mạnh đến mức bạn cần sử dụng một từ khóa cụ thể để xác định một thủ tục hoặc chương trình con và một từ khóa khác để xác định một hàm. Ví dụ, ngôn ngữ lập trình Visual Basic sử dụng Sub
và Function
để phân biệt giữa hai ngôn ngữ này .
Nói chung, một thủ tục là một khối mã được đặt tên thực hiện một tập hợp các hành động mà
không tính toán giá trị hoặc kết quả cuối cùng. Mặt khác, một hàm là một khối mã được đặt tên thực hiện một số hành động với mục đích tính toán giá trị hoặc kết quả cuối cùng, sau đó được gửi trở lại mã người gọi. Cả thủ tục và hàm đều có thể hoạt động dựa trên một tập giá trị đầu vào , thường được gọi là đối số .
Trong Python, các loại khối mã được đặt tên này được gọi là các hàm vì chúng luôn gửi một giá trị trở lại trình gọi. Tài liệu Python định nghĩa một hàm như sau:
Một loạt các câu lệnh trả về một số giá trị cho một người gọi. Nó cũng có thể được truyền không hoặc nhiều đối số có thể được sử dụng trong quá trình thực thi phần thân. ( Nguồn )
Mặc dù tài liệu chính thức nói rằng một hàm “trả về một số giá trị cho người gọi”, bạn sẽ sớm thấy rằng các hàm có thể trả về bất kỳ đối tượng Python nào cho mã người gọi.
Nói chung, một hàm nhận đối số (nếu có), thực hiện một số hoạt động và trả về một giá trị (hoặc đối tượng). Giá trị mà một hàm trả về cho người gọi thường được
gọi là giá trị trả về của hàm . Tất cả các hàm Python đều có giá trị trả về, rõ ràng hoặc ngầm định. Bạn sẽ đề cập đến sự khác biệt giữa các giá trị trả về rõ ràng và ngầm định ở phần sau trong hướng dẫn này.
Để viết một hàm Python, bạn cần một tiêu đề bắt đầu bằng def
từ khóa , theo sau là tên của hàm, danh sách tùy chọn các đối số được phân tách bằng
dấu phẩy bên trong một cặp dấu ngoặc đơn bắt buộc và dấu hai chấm cuối cùng.
Thành phần thứ hai của một hàm là khối mã hoặc nội dung của nó . Python định nghĩa các khối mã bằng cách sử dụng thụt đầu dòng thay vì dấu ngoặc begin
và end
từ khóa, v.v. Vì vậy, để xác định một hàm trong Python, bạn có thể sử dụng cú pháp sau:
def function_name(arg1, arg2,..., argN):
# Function's code goes here...
pass
Khi bạn đang mã hóa một hàm Python, bạn cần xác định tiêu đề với def
từ khóa, tên của hàm và danh sách các đối số trong dấu ngoặc đơn. Lưu ý rằng danh sách các đối số là tùy chọn, nhưng các dấu ngoặc đơn là bắt buộc về mặt cú pháp. Sau đó, bạn cần xác định khối mã của hàm, khối này sẽ bắt đầu một mức thụt lề về bên phải.
Trong ví dụ trên, bạn sử dụng một pass
câu lệnh . Loại câu
lệnh này hữu ích khi bạn cần một câu lệnh giữ chỗ trong mã của mình để làm cho nó chính xác về mặt cú pháp, nhưng bạn không cần phải thực hiện bất kỳ hành động nào. pass
các câu lệnh còn được gọi là thao tác null vì chúng không thực hiện bất kỳ hành động nào.
Lưu ý: Cú pháp đầy đủ để xác định các hàm và đối số của chúng nằm ngoài phạm vi của hướng dẫn này. Để có tài nguyên chuyên sâu về chủ đề này, hãy xem Định nghĩa hàm Python của riêng bạn .
Để sử dụng một chức năng, bạn cần phải gọi nó. Một lệnh gọi hàm bao gồm tên của hàm theo sau là các đối số của hàm trong dấu ngoặc đơn:
function_name(arg1, arg2, ..., argN)
Bạn sẽ chỉ cần chuyển các đối số cho một lệnh gọi hàm nếu hàm yêu cầu chúng. Mặt khác, dấu ngoặc đơn luôn được yêu cầu trong một lệnh gọi hàm. Nếu bạn quên chúng, thì bạn sẽ không gọi
hàm mà tham chiếu nó như một đối tượng hàm.
Để làm cho các hàm của bạn trả về một giá trị, bạn cần sử dụng câu lệnh Pythonreturn
. Đó là những gì bạn sẽ đề cập từ thời điểm này.
Tìm hiểu về Python returnTuyên Bố
Câu lệnh Pythonreturn
là
một câu lệnh đặc biệt mà bạn có thể sử dụng bên trong một hàm hoặc phương thức để gửi kết quả của hàm trở lại trình gọi. Một return
câu lệnh bao gồm return
từ khóa theo sau là giá trị trả về tùy chọn .
Giá trị trả về của một hàm Python có
thể là bất kỳ đối tượng Python nào. Mọi thứ trong Python đều là một đối tượng. Vì vậy, chức năng của bạn có thể trở lại các giá trị số ( int
, float
và complex
giá trị), các bộ sưu tập và trình tự của các đối tượng
( list
,tuple
, dictionary
, hoặc set
đối tượng), các đối tượng người dùng định nghĩa, các lớp học, chức năng, và thậm chí cả các module hoặc
gói .
Bạn có thể bỏ qua giá trị trả về của một hàm và sử dụng return
giá trị trả về không có giá trị trả về. Bạn cũng có thể bỏ qua toàn bộ return
câu lệnh. Trong cả hai trường hợp, giá trị trả về sẽ là None
.
Trong hai phần tiếp theo, bạn sẽ trình bày những kiến thức cơ
bản về cách return
hoạt động của câu lệnh và cách bạn có thể sử dụng nó để trả kết quả của hàm trở lại mã người gọi.
returnTuyên bố rõ ràng
Một câu lệnh rõ ràngreturn
ngay lập tức chấm dứt thực thi một hàm và gửi giá trị trả về trở lại mã người gọi. Để thêm một return
câu lệnh rõ ràng vào một hàm Python, bạn cần sử dụng return
theo sau là một giá trị trả về tùy chọn:
>>>
>>> def return_42():
... return 42 # An explicit return statement
...
>>> return_42() # The caller code gets 42
42
Khi
bạn xác định return_42()
, bạn thêm một return
câu lệnh rõ ràng ( return 42
) vào cuối khối mã của hàm. 42
là giá trị trả về rõ ràng của return_42()
. Điều này có nghĩa là bất kỳ lúc nào bạn gọi return_42()
, chức năng sẽ gửi 42
lại cho người gọi.
Lưu ý: Bạn có thể sử dụng các return
câu lệnh rõ ràng có hoặc không có giá trị trả về. Nếu bạn xây dựng một return
câu lệnh mà không chỉ định giá trị trả về, thì bạn
sẽ hoàn toàn trả về None
.
Nếu bạn xác định một hàm bằng một return
câu lệnh rõ ràng có giá trị trả về rõ ràng, thì bạn có thể sử dụng giá trị trả về đó trong bất kỳ biểu thức nào:
>>>
>>> num = return_42()
>>> num
42
>>> return_42() * 2
84
>>> return_42() + 5
47
Vì return_42()
trả về một giá trị số, bạn có thể sử dụng giá trị đó trong một biểu thức toán học hoặc bất kỳ loại biểu thức nào khác trong đó giá trị có ý nghĩa logic hoặc mạch lạc. Đây là cách mã người gọi có thể tận dụng giá
trị trả về của một hàm.
Lưu ý rằng bạn chỉ có thể sử dụng một return
câu lệnh bên trong định nghĩa hàm hoặc phương thức. Nếu bạn sử dụng nó ở bất kỳ nơi nào khác, thì bạn sẽ nhận được SyntaxError
:
>>>
>>> return 42
File "<stdin>", line 1
SyntaxError: 'return' outside function
Khi bạn sử dụng return
bên ngoài một hàm hoặc
phương thức, bạn sẽ nhận được SyntaxError
thông báo rằng không thể sử dụng câu lệnh bên ngoài một hàm.
Bạn có thể sử dụng bất kỳ đối tượng Python nào làm giá trị trả về. Vì mọi thứ trong Python là một đối tượng, bạn có thể trả về chuỗi , danh sách, bộ giá trị, từ điển, hàm, lớp, phiên
bản , đối tượng do người dùng định nghĩa và thậm chí cả mô-đun hoặc gói.
Ví dụ: giả sử bạn cần viết một hàm lấy danh sách các số nguyên và trả về một danh sách chỉ chứa các số chẵn trong danh sách ban đầu. Đây là một cách mã hóa hàm này:
>>>
>>> def get_even(numbers):
... even_nums = [num for num in numbers if not num % 2]
... return even_nums
...
>>> get_even([1, 2, 3, 4, 5, 6])
[2, 4, 6]
get_even()
sử dụng khả năng hiểu danh sách để tạo danh sách lọc ra các số lẻ trong bản gốc numbers
. Sau đó, hàm trả về danh sách kết quả, chỉ chứa các số chẵn.
Một thực tế phổ biến là sử dụng kết quả của một biểu thức làm giá trị trả về trong một return
câu lệnh. Để áp dụng ý tưởng này, bạn có
thể viết lại get_even()
như sau:
>>>
>>> def get_even(numbers):
... return [num for num in numbers if not num % 2]
...
>>> get_even([1, 2, 3, 4, 5, 6])
[2, 4, 6]
Khả năng hiểu danh sách được đánh giá và sau đó hàm trả về với danh sách kết quả. Lưu ý rằng bạn chỉ có thể sử dụng các biểu thức trong một return
câu lệnh. Biểu thức khác với các câu lệnh như điều kiện hoặc vòng lặp .
Lưu ý: Mặc dù các từ khóa list
hiểu được xây dựng bằng cách sử dụng for
và (tùy chọn) if
, chúng được coi là biểu thức chứ không phải là câu lệnh. Đó là lý do tại sao bạn có thể sử dụng chúng trong một return
câu lệnh.
Đối
với một ví dụ khác, giả sử bạn cần tính giá trị trung bình của một mẫu các giá trị số. Để làm điều đó, bạn cần chia tổng các giá trị cho số giá trị. Dưới đây là một ví dụ sử dụng các hàm tích hợp sum()
và len()
:
>>>
>>> def mean(sample):
... return sum(sample) / len(sample)
...
>>> mean([1, 2, 3, 4])
2.5
Trong đó mean()
, bạn
không sử dụng một biến cục bộ để lưu trữ kết quả của phép tính. Thay vào đó, bạn sử dụng trực tiếp biểu thức làm giá trị trả về. Đầu tiên Python đánh giá biểu thức sum(sample) / len(sample)
và sau đó trả về kết quả đánh giá, trong trường hợp này là giá trị 2.5
.
returnTuyên bố ngầm
Một hàm Python sẽ luôn có giá trị trả về. Không có khái niệm về thủ tục hoặc quy trình trong
Python. Vì vậy, nếu bạn không sử dụng rõ ràng giá trị trả về trong một return
câu lệnh hoặc nếu bạn hoàn toàn bỏ qua return
câu lệnh, thì Python sẽ mặc nhiên trả về một giá trị mặc định cho bạn. Giá trị trả về mặc định đó sẽ luôn là None
.
Giả sử bạn đang viết một hàm thêm 1
vào một số x
, nhưng bạn quên cung cấp một return
câu lệnh. Trong trường hợp này, bạn sẽ nhận được một câu lệnh ngầmreturn
sử dụng None
làm
giá trị trả về:
>>>
>>> def add_one(x):
... # No return statement at all
... result = x + 1
...
>>> value = add_one(5)
>>> value
>>> print(value)
None
Nếu bạn không cung cấp một return
câu lệnh rõ ràng với giá trị trả về rõ ràng, thì Python sẽ cung cấp một return
câu lệnh ngầm sử dụng None
làm giá trị trả về. Trong ví dụ trên, add_one()
thêm 1
vào x
và lưu trữ giá trị vào result
nhưng nó không trả về result
. Đó là lý do tại sao bạn nhận được value = None
thay vì value = 6
. Để
khắc phục sự cố, bạn cần thực hiện return result
hoặc trực tiếp return x + 1
.
Một ví dụ về một hàm trả về None
là print()
. Mục tiêu của chức năng này là in các đối tượng ra tệp dòng văn bản, thường là đầu ra tiêu chuẩn (màn hình của bạn). Vì vậy, hàm này không cần một return
câu lệnh rõ
ràng vì nó không trả về bất kỳ điều gì hữu ích hoặc có ý nghĩa:
>>>
>>> return_value = print("Hello, World")
Hello, World
>>> print(return_value)
None
Cuộc gọi print()
in Hello, World
ra màn hình. Vì đây là mục đích của nó print()
, nên hàm không cần trả về bất kỳ thứ gì hữu ích, vì vậy bạn sẽ nhận được None
dưới dạng giá trị trả về.
Lưu ý: Trình thông dịch Python không hiển thị None
. Vì
vậy, để hiển thị giá trị trả về None
trong một phiên tương tác , bạn cần phải sử dụng rõ ràng print()
.
Bất kể hàm của bạn dài và phức tạp như thế nào, bất kỳ hàm nào không có return
câu lệnh rõ ràng hoặc hàm có return
câu lệnh không có giá trị trả về sẽ trả về None
.
Trả lại và in
Nếu bạn đang làm việc trong một
phiên tương tác, thì bạn có thể nghĩ rằng việc in một giá trị và trả về một giá trị là các thao tác tương đương. Hãy xem xét hai chức năng sau và đầu ra của chúng:
>>>
>>> def print_greeting():
... print("Hello, World")
...
>>> print_greeting()
Hello, World
>>> def return_greeting():
... return "Hello, World"
...
>>> return_greeting()
'Hello, World'
Cả hai chức năng dường như làm cùng một điều. Trong cả hai trường hợp, bạn thấy Hello, World
được in trên màn hình của mình. Chỉ có một sự khác biệt nhỏ có thể nhìn thấy — các dấu ngoặc kép trong ví dụ thứ hai. Nhưng hãy xem điều gì sẽ xảy ra nếu bạn trả về
một kiểu dữ liệu khác, giả sử một int
đối tượng:
>>>
>>> def print_42():
... print(42)
...
>>> print_42()
42
>>> def return_42():
... return 42
...
>>> return_42()
42
Không có sự khác biệt rõ ràng bây giờ. Trong cả hai trường hợp, bạn có thể thấy 42
trên màn hình của mình. Hành vi đó có thể gây nhầm lẫn nếu bạn mới bắt đầu với Python. Bạn có thể nghĩ rằng trả lại và in một giá trị là các hành động tương đương.
Bây
giờ, giả sử bạn đang hiểu sâu hơn về Python và bạn đang bắt đầu viết tập lệnh đầu tiên của mình. Bạn mở một trình soạn thảo văn bản và nhập mã sau:
1def add(a, b):
2 result = a + b
3 return result
4
5add(2, 2)
add()
lấy hai số, thêm chúng và trả về kết quả. Trên dòng 5 , bạn gọi add()
đến tổng 2
cộng 2
. Vì bạn vẫn đang tìm hiểu sự khác biệt giữa trả về và in một giá trị, bạn có thể mong đợi tập lệnh của mình được in 4
ra màn hình. Tuy nhiên, đó không phải là
những gì xảy ra và bạn không nhận được gì trên màn hình của mình.
Hãy thử nó một mình. Lưu tập lệnh của bạn vào một tệp được gọi adding.py
và chạy nó từ dòng lệnh của bạn như sau:
Nếu bạn chạy adding.py
từ dòng lệnh của mình, thì bạn sẽ không thấy bất kỳ kết quả nào trên màn hình của mình. Đó là bởi vì khi bạn chạy một tập lệnh, các giá trị trả về của các hàm mà bạn gọi trong tập lệnh
không được in ra màn hình giống như chúng làm trong một phiên tương tác.
Nếu bạn muốn tập lệnh của mình hiển thị kết quả của cuộc gọi add()
trên màn hình, thì bạn cần phải gọi một cách rõ ràng print()
. Kiểm tra bản cập nhật sau của adding.py
:
1def add(a, b):
2 result = a + b
3 return result
4
5print(add(2, 2))
Bây giờ, khi bạn chạy adding.py
, bạn sẽ thấy số 4
trên màn
hình của mình.
Vì vậy, nếu bạn đang làm việc trong một phiên tương tác, thì Python sẽ hiển thị kết quả của bất kỳ lệnh gọi hàm nào trực tiếp lên màn hình của bạn. Nhưng nếu bạn đang viết một tập lệnh và bạn muốn xem giá trị trả về của một hàm, thì bạn cần phải sử dụng một cách rõ ràng print()
.
Trả lại nhiều giá trị
Bạn có thể sử dụng một return
câu lệnh để trả về nhiều giá trị từ một hàm. Để làm điều đó, bạn chỉ cần cung cấp một số giá trị trả về được
phân tách bằng dấu phẩy.
Ví dụ: giả sử bạn cần viết một hàm lấy một mẫu dữ liệu số và trả về một bản tóm tắt các phép đo thống kê. Để viết mã cho hàm đó, bạn có thể sử dụng mô-đun chuẩn Python statistics
, mô-đun này cung cấp một số hàm để tính toán thống kê toán học của dữ liệu số.
Đây là cách triển khai chức năng của bạn có thể:
import statistics as st
def describe(sample):
return st.mean(sample), st.median(sample), st.mode(sample)
Trong đó describe()
, bạn tận
dụng khả năng của Python để trả về nhiều giá trị trong một return
câu lệnh bằng cách trả về giá trị trung bình, giá trị trung bình và chế độ của mẫu cùng một lúc. Lưu ý rằng, để trả về nhiều giá trị, bạn chỉ cần viết chúng trong danh sách được phân tách bằng dấu phẩy theo thứ tự bạn muốn chúng được trả về.
Khi bạn đã mã hóa describe()
, bạn có thể tận dụng tính năng mạnh mẽ của Python được gọi là giải nén có thể lặp lại để giải nén ba biện pháp thành ba biến riêng biệt hoặc bạn chỉ có thể lưu trữ mọi thứ trong một biến:
>>>
>>> sample = [10, 2, 4, 7, 9, 3, 9, 8, 6, 7]
>>> mean, median, mode = describe(sample)
>>> mean
6.5
>>> median
7.0
>>> mode
7
>>> desc = describe(sample)
>>> desc
(6.5, 7.0, 7)
>>> type(desc)
<class 'tuple'>
Ở đây, bạn
giải nén ba giá trị trở lại của describe()
thành các biến mean
, median
và mode
. Lưu ý rằng trong ví dụ cuối cùng, bạn lưu trữ tất cả các giá trị trong một biến duy nhất, biến desc
này hóa ra là một Python tuple
.
Lưu ý: Bạn có thể tạo Python tuple
bằng cách chỉ định một số giá trị được phân tách bằng dấu phẩy cho một biến duy nhất. Không cần sử dụng dấu ngoặc đơn để tạo một tuple
. Đó là lý do tại sao nhiều giá trị
trả về được đóng gói trong a tuple
.
Hàm dựng sẵn divmod()
cũng là một ví dụ về hàm trả về nhiều giá trị. Hàm nhận hai số (không phức tạp) làm đối số và trả về hai số, thương của hai giá trị đầu vào và phần còn lại của phép chia:
>>>
>>> divmod(15, 3)
(5, 0)
>>> divmod(8, 3)
(2, 2)
Lệnh gọi divmod()
trả về một bộ giá trị chứa thương và phần dư là kết quả của việc chia hai số không
phức được cung cấp dưới dạng đối số. Đây là một ví dụ về một hàm có nhiều giá trị trả về.
Sử dụng returnTuyên bố Python : Các phương pháp hay nhất
Cho đến nay, bạn đã trình bày những kiến thức cơ bản về cách return
hoạt động của câu lệnh Python . Bây giờ bạn biết cách viết các hàm trả về một hoặc nhiều giá trị cho trình gọi. Ngoài ra, bạn đã biết rằng nếu bạn không thêm một return
câu lệnh rõ ràng với giá trị trả về
rõ ràng vào một hàm nhất định, thì Python sẽ thêm nó cho bạn. Giá trị đó sẽ là None
.
Trong phần này, bạn sẽ trình bày một số ví dụ sẽ hướng dẫn bạn qua một tập hợp các phương pháp lập trình tốt để sử dụng return
câu lệnh một cách hiệu quả . Những thực hành này sẽ giúp bạn viết các hàm dễ đọc, dễ bảo trì, mạnh mẽ và hiệu quả hơn trong Python.
Trả lại một Nonecách rõ ràng
Một số lập trình viên dựa vào return
câu lệnh ngầm mà
Python thêm vào bất kỳ hàm nào mà không có một câu lệnh rõ ràng. Điều này có thể gây nhầm lẫn cho các nhà phát triển đến từ các ngôn ngữ lập trình khác, trong đó một hàm không có giá trị trả về được gọi là một thủ tục .
Có những tình huống mà bạn có thể thêm một cách rõ ràng return None
vào các chức năng của mình. Tuy nhiên, trong các tình
huống khác, bạn có thể dựa vào hành vi mặc định của Python:
Nếu hàm của bạn thực hiện các hành động nhưng không có return
giá trị rõ ràng và hữu ích , thì bạn có thể bỏ qua việc trả về None
vì làm điều đó sẽ chỉ là thừa và khó hiểu. Bạn cũng có thể sử dụng một return
giá trị trống không có giá trị trả về chỉ để làm rõ ý định quay lại của bạn từ hàm.
Nếu hàm của bạn có nhiều return
câu lệnh và trả về None
là một tùy
chọn hợp lệ, thì bạn nên xem xét việc sử dụng rõ ràng return None
thay vì dựa vào hành vi mặc định của Python.
Những thực hành này có thể cải thiện khả năng đọc và khả năng bảo trì của mã của bạn bằng cách truyền đạt ý định của bạn một cách rõ ràng.
Khi nói đến việc quay lại None
, bạn có thể sử dụng một trong ba cách tiếp cận có thể có:
- Bỏ qua
return
câu lệnh và dựa vào hành vi trả về mặc định None
. - Sử dụng
return
giá
trị trống không có giá trị trả về, giá trị này cũng trả về None
. - Trả lại
None
một cách rõ ràng.
Đây là cách điều này hoạt động trong thực tế:
>>>
>>> def omit_return_stmt():
... # Omit the return statement
... pass
...
>>> print(omit_return_stmt())
None
>>> def bare_return():
... # Use a bare return
... return
...
>>> print(bare_return())
None
>>> def return_none_explicitly():
... # Return None explicitly
... return None
...
>>> print(return_none_explicitly())
None
Việc có trở lại None
một cách rõ ràng hay không là một quyết định cá nhân. Tuy nhiên, bạn nên xem xét rằng trong một số
trường hợp, một thông báo rõ ràng return None
có thể tránh được các vấn đề về khả năng bảo trì. Điều này đặc biệt đúng đối với các nhà phát triển đến từ các ngôn ngữ lập trình khác không hoạt động như Python.
Ghi nhớ giá trị trả lại
Khi viết các hàm tùy chỉnh, bạn có thể vô tình quên trả về một giá trị từ một hàm. Trong trường hợp này, Python sẽ trả về None
cho bạn. Điều này có thể gây ra các lỗi nhỏ mà một nhà phát triển Python mới bắt đầu có thể khó hiểu và gỡ lỗi .
Bạn có thể tránh vấn đề này bằng cách viết return
câu lệnh ngay sau tiêu đề của hàm. Sau đó, bạn có thể thực hiện một lần vượt qua thứ hai để viết phần thân của hàm. Đây là một mẫu mà bạn có thể sử dụng khi mã hóa các hàm Python của mình:
def template_func(args):
result = 0 # Initialize the return value
# Your code goes here...
return result # Explicitly return the result
Nếu bạn đã quen với việc bắt đầu các chức năng của mình như thế này, thì rất có thể bạn sẽ không còn bỏ sót return
câu
lệnh nữa. Với cách tiếp cận này, bạn có thể viết phần thân của hàm, kiểm tra nó và đổi tên các biến khi bạn biết rằng hàm hoạt động.
Thực hành này có thể tăng năng suất của bạn và làm cho các chức năng của bạn ít bị lỗi hơn. Nó cũng có thể giúp bạn tiết kiệm rất nhiều thời gian gỡ lỗi .
Tránh các biểu thức phức tạp
Như bạn đã thấy trước đây, một thực tế phổ biến là sử dụng kết
quả của một biểu thức làm giá trị trả về trong các hàm Python. Nếu biểu thức bạn đang sử dụng quá phức tạp, thì phương pháp này có thể dẫn đến các hàm khó hiểu, khó gỡ lỗi và duy trì.
Ví dụ: nếu bạn đang thực hiện một phép tính phức tạp, thì việc tính toán dần dần kết quả cuối cùng bằng cách sử dụng các biến tạm thời có tên có nghĩa sẽ dễ đọc hơn .
Hãy xem xét hàm sau để tính toán phương
sai của một mẫu dữ liệu số:
>>>
>>> def variance(data, ddof=0):
... mean = sum(data) / len(data)
... return sum((x - mean) ** 2 for x in data) / (len(data) - ddof)
...
>>> variance([3, 4, 7, 5, 6, 2, 9, 4, 1, 3])
5.24
Cách diễn đạt mà bạn sử dụng ở đây khá phức tạp và khó hiểu. Cũng khó gỡ lỗi vì bạn đang thực hiện nhiều thao tác trong một biểu thức. Để giải quyết vấn đề cụ thể này, bạn có thể tận dụng phương pháp phát triển gia tăng để cải thiện khả năng đọc của hàm.
Hãy xem cách triển khai thay
thế sau đây của variance()
:
>>>
>>> def variance(data, ddof=0):
... n = len(data)
... mean = sum(data) / n
... total_square_dev = sum((x - mean) ** 2 for x in data)
... return total_square_dev / (n - ddof)
...
>>> variance([3, 4, 7, 5, 6, 2, 9, 4, 1, 3])
5.24
Trong lần triển khai thứ hai này variance()
, bạn tính toán phương sai trong một số bước. Mỗi bước được biểu diễn bằng một biến tạm thời có tên có nghĩa.
Các biến tạm thời như n
, mean
và total_square_dev
thường hữu ích khi gỡ lỗi mã của bạn. Ví dụ, nếu có gì đó không ổn với một trong số chúng, thì bạn có thể gọi print()
để biết điều gì đang xảy ra trước khi return
câu
lệnh chạy.
Nói chung, bạn nên tránh sử dụng các biểu thức phức tạp trong return
câu lệnh của mình . Thay vào đó, bạn có thể chia mã của mình thành nhiều bước và sử dụng các biến tạm thời cho mỗi bước. Sử dụng các biến tạm thời có thể giúp mã của bạn dễ gỡ lỗi, hiểu và bảo trì hơn.
Trả lại giá trị so với sửa đổi toàn cầu
Các hàm không có return
câu lệnh rõ ràng với giá trị trả về có ý nghĩa thường tạo ra các hành động có tác dụng phụ . Một tác dụng phụ có thể là, ví dụ, in thứ gì đó ra màn hình, sửa đổi một biến toàn cục , cập nhật trạng thái của một đối tượng, ghi một số văn
bản vào tệp , v.v.
Việc sửa đổi các biến toàn cục thường được coi là một phương pháp lập trình tồi. Cũng giống như các chương trình có biểu thức phức tạp, các chương trình sửa đổi các biến toàn cục có thể khó gỡ lỗi, hiểu và bảo trì.
Khi bạn sửa đổi một biến toàn cục, bạn có khả năng ảnh hưởng đến tất cả các hàm, lớp, đối tượng và bất kỳ phần nào khác của chương trình dựa trên biến toàn cục đó.
Để hiểu một chương trình sửa đổi các biến toàn cục, bạn cần phải biết
tất cả các phần của chương trình có thể xem, truy cập và thay đổi các biến đó. Vì vậy, thực tiễn tốt khuyên bạn nên viết các hàm tự chứa lấy một số đối số và trả về một giá trị hữu ích (hoặc các giá trị) mà không gây ra bất kỳ tác dụng phụ nào đối với các biến toàn cục.
Ngoài ra, các hàm có return
câu lệnh rõ ràng trả về giá trị có nghĩa dễ kiểm tra hơn các hàm sửa đổi hoặc
cập nhật các biến toàn cục.
Ví dụ sau đây cho thấy một hàm thay đổi một biến toàn cục. Hàm sử dụng global
câu lệnh , cũng được coi là một phương pháp lập trình tồi trong Python:
>>>
>>> counter = 0
>>> def increment():
... global counter
... counter += 1
...
>>> increment()
>>> counter
1
Trong ví dụ này, trước tiên bạn tạo
một biến toàn cục counter
, với giá trị ban đầu là 0
. Bên trong increment()
, bạn sử dụng một global
câu lệnh để cho hàm biết rằng bạn muốn sửa đổi một biến toàn cục. Báo cáo kết quả cuối cùng increments counter
bằng 1
.
Kết quả của việc gọi increment()
sẽ phụ thuộc vào giá trị ban đầu của counter
. Các giá trị ban đầu khác nhau cho counter
sẽ tạo ra các kết quả khác nhau, do đó, kết quả của hàm không thể được kiểm soát bởi
chính hàm.
Để tránh loại hành vi này, bạn có thể viết một hàm chứa các increment()
đối số và trả về một giá trị nhất quán chỉ phụ thuộc vào các đối số đầu vào:
>>>
>>> counter = 0
>>> def increment(var):
... return var + 1
...
>>> increment(counter)
1
>>> counter
0
>>> # Explicitly assign a new value to counter
>>> counter = increment(counter)
>>> counter
1
Bây giờ kết quả của việc gọi increment()
chỉ phụ thuộc vào các đối số đầu vào hơn là vào giá trị ban đầu của counter
. Điều này làm cho chức năng mạnh mẽ hơn và dễ kiểm tra hơn.
Ngoài ra, khi bạn cần cập nhật counter
, bạn có thể thực hiện việc này một
cách rõ ràng bằng một cuộc gọi tới increment()
. Bằng cách này, bạn sẽ có nhiều quyền kiểm soát hơn đối với những gì đang xảy ra counter
trong suốt mã của mình.
Nói chung, bạn nên tránh các hàm sửa đổi các biến toàn cục. Nếu có thể, hãy cố gắng viết các hàm độc lập với một return
câu lệnh rõ ràng trả về một giá trị có ý nghĩa và mạch lạc.
Sử
dụng có returnđiều kiện
Các hàm Python không bị hạn chế chỉ có một return
câu lệnh duy nhất . Nếu một hàm nhất định có nhiều hơn một return
câu lệnh, thì câu lệnh đầu tiên gặp phải sẽ xác định thời điểm kết thúc thực thi của hàm và cả giá trị trả về của nó.
Một cách phổ biến để viết các hàm với nhiều return
câu lệnh là sử dụng các câu lệnh điều
kiện cho phép bạn cung cấp các return
câu lệnh khác nhau tùy thuộc vào kết quả đánh giá một số điều kiện.
Giả sử bạn cần mã hóa một hàm nhận một số và trả về giá trị tuyệt đối của nó. Nếu số lớn hơn 0
, thì bạn sẽ trả về cùng một số. Nếu số nhỏ hơn 0
, thì bạn sẽ trả về giá trị ngược lại hoặc không âm của nó.
Đây là cách triển khai có thể thực hiện cho chức năng này:
>>>
>>> def my_abs(number):
... if number > 0:
... return number
... elif number < 0:
... return -number
...
>>> my_abs(-15)
15
>>> my_abs(15)
15
my_abs()
có
hai return
câu lệnh rõ ràng , mỗi câu được gói gọn trong một if
câu lệnh riêng . Nó cũng có một return
tuyên bố ngầm . Nếu number
xảy ra 0
, thì không có điều kiện nào là đúng và hàm kết thúc mà không nhấn vào bất kỳ return
câu lệnh rõ ràng nào . Khi điều này xảy ra, bạn sẽ tự động nhận được None
.
Hãy
xem lời gọi sau để my_abs()
sử dụng 0
làm đối số:
>>>
>>> print(my_abs(0))
None
Khi bạn gọi my_abs()
bằng cách sử dụng 0
làm đối số, None
kết quả là bạn sẽ nhận được . Đó là bởi vì luồng thực thi đến cuối hàm mà không đạt được bất kỳ return
câu lệnh rõ ràng nào . Thật không may, giá trị tuyệt đối của 0
là 0
, không phải None
.
Để khắc phục sự cố này, bạn có thể
thêm return
câu lệnh thứ ba , trong một elif
mệnh đề mới hoặc trong một else
mệnh đề cuối cùng :
>>>
>>> def my_abs(number):
... if number > 0:
... return number
... elif number < 0:
... return -number
... else:
... return 0
...
>>> my_abs(0)
0
>>> my_abs(-15)
15
>>> my_abs(15)
15
Bây giờ, my_abs()
kiểm tra mọi điều kiện có thể, number > 0
, number < 0
, và number == 0
. Mục đích của ví dụ này là để chỉ ra rằng khi bạn đang sử dụng câu lệnh điều kiện để cung
cấp nhiều return
câu lệnh, bạn cần đảm bảo rằng mọi tùy chọn có thể có đều có return
câu lệnh riêng . Nếu không, chức năng của bạn sẽ có một lỗi ẩn.
Cuối cùng, bạn có thể triển khai my_abs()
theo cách ngắn gọn, hiệu quả và Pythonic hơn bằng cách sử dụng một if
câu lệnh duy nhất :
>>>
>>> def my_abs(number):
... if number < 0:
... return -number
... return number
...
>>> my_abs(0)
0
>>> my_abs(-15)
15
>>> my_abs(15)
15
Trong
trường hợp này, hàm của bạn truy cập vào return
câu lệnh đầu tiên nếu number < 0
. Trong tất cả các trường hợp khác, cho dù number > 0
hoặc number == 0
, nó đánh vào return
câu lệnh thứ hai . Với cách triển khai mới này, chức năng của bạn trông tốt hơn rất nhiều. Nó dễ đọc hơn, ngắn gọn và hiệu quả hơn.
Lưu ý: Có một hàm Python
tích hợp sẵn tiện lợi được gọi abs()
để tính toán giá trị tuyệt đối của một số. Chức năng trong ví dụ trên chỉ nhằm minh họa cho điểm đang thảo luận.
Nếu bạn đang sử dụng các if
câu lệnh để cung cấp một số return
câu lệnh, thì bạn không cần một else
mệnh đề để bao hàm điều kiện cuối cùng. Chỉ cần thêm một return
câu lệnh vào cuối khối mã của hàm và ở mức thụt lề
đầu tiên.
Trở lại TruehoặcFalse
Một trường hợp sử dụng phổ biến khác cho sự kết hợp của if
và các return
câu lệnh là khi bạn đang mã hóa một vị từ hoặc một hàm có giá trị Boolean . Loại hàm này trả về một trong hai True
hoặc False
theo một
điều kiện nhất định.
Ví dụ, nói rằng bạn cần phải viết một chức năng mà phải mất hai số nguyên, a
và b
, và trả về True
nếu a
là chia hết cho b
. Nếu không, hàm sẽ trả về False
. Đây là cách triển khai khả thi:
>>>
>>> def is_divisible(a, b):
... if not a % b:
... return True
... return False
...
>>> is_divisible(4, 2)
True
>>> is_divisible(7, 4)
False
is_divisible()
trả về True
nếu phần còn lại của phép chia a
cho b
bằng 0
. Nếu không, nó sẽ trả về False
. Lưu
ý rằng trong Python, một 0
giá trị là falsy , vì vậy bạn cần phải sử dụng các not
toán tử để phủ nhận giá trị thật của tình trạng này.
Đôi khi bạn sẽ viết các hàm vị từ liên quan đến các toán tử như sau:
- Các toán tử so sánh
==
, !=
, >
, >=
, <
, và<=
- Nhà điều hành thành viên
in
- Nhà điều hành danh
tính
is
- Toán tử Boolean
not
Trong những trường hợp này, bạn có thể sử dụng trực tiếp biểu thức Boolean trong return
câu lệnh của mình . Điều này có thể xảy ra vì các toán tử này trả về một trong hai True
hoặc False
. Theo ý tưởng này, đây là một
triển khai mới của is_divisible()
:
>>>
>>> def is_divisible(a, b):
... return not a % b
...
>>> is_divisible(4, 2)
True
>>> is_divisible(7, 4)
False
Nếu a
chia hết cho b
thì a % b
trả về 0
, điều này là sai trong Python. Vì vậy, để quay lại True
, bạn cần sử dụng not
toán tử.
Lưu ý: Python tuân theo một tập hợp các quy tắc để xác định giá trị chân lý của
một đối tượng.
Ví dụ, các đối tượng sau được coi là giả :
- Hằng số thích
None
vàFalse
- Loại số với giá trị zero như
0
, 0.0
, 0j
, Decimal(0)
,
vàFraction(0, 1)
- Chuỗi rỗng và bộ sưu tập thích
""
, ()
, []
, {}
, set()
, vàrange(0)
- Các đối tượng triển khai
__bool__()
với giá trị trả về False
hoặc __len__()
với giá trị trả về là0
Bất kỳ đối tượng nào khác sẽ được coi là trung thực.
Mặt khác, nếu bạn cố gắng sử dụng các điều kiện liên quan đến toán tử Boolean như or
và and
theo cách bạn đã thấy trước đây, thì các hàm vị từ của bạn sẽ không hoạt động chính xác. Đó là bởi vì các toán tử này hoạt động khác nhau. Chúng trả về một trong các toán hạng trong điều kiện thay vì True
hoặc False
:
>>>
>>> 0 and 1
0
>>> 1 and 2
2
>>> 1 or 2
1
>>> 0 or 1
1
Nói chung, and
trả về toán hạng sai đầu tiên hoặc toán hạng cuối
cùng. Mặt khác, or
trả về toán hạng đúng đầu tiên hoặc toán hạng cuối cùng. Vì vậy, để viết một vị từ có liên quan đến một trong các toán tử này, bạn sẽ cần sử dụng một if
câu lệnh rõ ràng hoặc một lệnh gọi hàm tích hợp sẵn bool()
.
Giả sử bạn muốn viết một hàm vị từ nhận hai giá trị và trả về True
nếu cả hai đều đúng và False
ngược lại. Đây
là cách tiếp cận đầu tiên của bạn đối với chức năng này:
>>>
>>> def both_true(a, b):
... return a and b
...
>>> both_true(1, 2)
2
Vì and
trả về toán hạng thay vì True
hoặc False
, nên hàm của bạn không hoạt động chính xác. Có ít nhất ba khả năng để khắc phục sự cố này:
- Một tuyên bố rõ ràng
if
- Một biểu thức điều kiện (toán tử bậc ba)
- Hàm Python tích hợp sẵn
bool()
Nếu bạn sử dụng cách tiếp cận đầu tiên, thì bạn có thể viết both_true()
như
sau:
>>>
>>> def both_true(a, b):
... if a and b:
... return True
... return False
...
>>> both_true(1, 2)
True
>>> both_true(1, 0)
False
Câu if
lệnh kiểm tra nếu a
và b
cả hai đều là trung thực. Nếu vậy, sau đó both_true()
quay trở lại True
. Nếu không, nó sẽ trả về False
.
Mặt khác, nếu bạn sử dụng biểu thức điều kiện Python hoặc toán tử bậc ba, thì bạn có thể viết hàm vị ngữ của mình như sau:
>>>
>>> def both_true(a, b):
... return True if a and b else False
...
>>> both_true(1, 2)
True
>>> both_true(1, 0)
False
Ở đây, bạn sử dụng biểu thức điều kiện để cung cấp giá trị
trả về cho both_true()
. Biểu thức điều kiện được đánh giá True
nếu cả hai a
và b
đều đúng. Nếu không, kết quả cuối cùng là False
.
Cuối cùng, nếu bạn sử dụng bool()
, thì bạn có thể viết mã both_true()
như sau:
>>>
>>> def both_true(a, b):
... return bool(a and b)
...
>>> both_true(1, 2)
True
>>> both_true(1, 0)
False
bool()
trả về True
nếu a
và b
đúng
và False
nếu không. Việc sử dụng cách tiếp cận nào để giải quyết vấn đề này là tùy thuộc vào bạn. Tuy nhiên, giải pháp thứ hai có vẻ dễ đọc hơn. Bạn nghĩ sao?
Vòng lặp ngắn
Một return
câu lệnh bên trong một vòng lặp thực hiện một số loại ngắn mạch . Nó phá vỡ việc thực thi vòng lặp và làm cho hàm trả về ngay lập tức. Để hiểu rõ hơn về hành vi này, bạn có thể viết một hàm mô phỏng any()
. Hàm tích hợp này nhận một hàm có thể lặp lại và trả về True
nếu ít nhất một trong các mục của nó là trung thực.
Để mô phỏng any()
, bạn có thể viết mã một hàm như sau:
>>>
>>> def my_any(iterable):
... for item in iterable:
... if item:
... # Short-circuit
... return True
... return False
>>> my_any([0, 0, 1, 0, 0])
True
>>> my_any([0, 0, 0, 0, 0])
False
Nếu bất kỳ item
trong nào iterable
là đúng, thì luồng thực thi sẽ đi vào trong if
khối. Câu return
lệnh phá vỡ vòng lặp và trả về ngay lập
tức với giá trị trả về là True
. Nếu không có giá trị nào iterable
là true, thì my_any()
trả về False
.
Chức năng này thực hiện đánh giá ngắn mạch . Ví dụ: giả sử bạn chuyển một tệp có thể lặp lại chứa một triệu mục. Nếu mục đầu tiên trong mục có thể lặp lại đó là true, thì vòng lặp chỉ chạy một lần chứ không phải một triệu lần. Điều này có
thể giúp bạn tiết kiệm rất nhiều thời gian xử lý khi chạy mã của mình.
Điều quan trọng cần lưu ý là để sử dụng một return
câu lệnh bên trong một vòng lặp, bạn cần phải gói câu lệnh đó trong một if
câu lệnh. Nếu không, vòng lặp sẽ luôn bị phá vỡ trong lần lặp đầu tiên của nó.
Nhận biết mã chết
Ngay sau khi một hàm truy cập vào một return
câu lệnh, nó sẽ kết thúc mà không thực thi bất kỳ mã nào tiếp theo. Do đó, mã xuất hiện sau return
câu
lệnh của hàm thường được gọi là mã chết . Trình thông dịch Python hoàn toàn bỏ qua mã chết khi chạy các chức năng của bạn. Vì vậy, có loại mã đó trong một hàm là vô ích và khó hiểu.
Hãy xem xét hàm sau, hàm này thêm mã vào sau return
câu lệnh của nó :
>>>
>>> def dead_code():
... return 42
... # Dead code
... print("Hello, World")
...
>>> dead_code()
42
Câu lệnh print("Hello, World")
trong ví dụ này sẽ không bao giờ thực thi vì câu lệnh đó xuất hiện sau return
câu lệnh của hàm . Xác
định mã chết và loại bỏ nó là một phương pháp hay mà bạn có thể áp dụng để viết các hàm tốt hơn.
Cần lưu ý rằng nếu bạn đang sử dụng câu lệnh điều kiện để cung cấp nhiều return
câu lệnh, thì bạn có thể có mã sau một return
câu lệnh sẽ không bị chết miễn là nó nằm ngoài if
câu lệnh:
>>>
>>> def no_dead_code(condition):
... if condition:
... return 42
... print("Hello, World")
...
>>> no_dead_code(True)
42
>>> no_dead_code(False)
Hello, World
Mặc dù lệnh gọi đến print()
sau một return
câu lệnh, nó không phải là mã chết. Khi condition
được đánh giá
đến False
, print()
cuộc gọi sẽ chạy và bạn sẽ được Hello, World
in ra màn hình.
Trả lại nhiều đối tượng được đặt tên
Khi bạn đang viết một hàm trả về nhiều giá trị trong một return
câu lệnh, bạn có thể cân nhắc sử dụng một collections.namedtuple
đối tượng để làm cho các hàm
của bạn dễ đọc hơn. namedtuple
là một lớp tập hợp trả về một lớp con tuple
có các trường hoặc thuộc tính. Bạn có thể truy cập các thuộc tính đó bằng ký hiệu dấu chấm hoặc thao tác lập chỉ mục .
Trình khởi tạo của namedtuple
có một số đối số. Tuy
nhiên, để bắt đầu sử dụng namedtuple
mã của bạn, bạn chỉ cần biết về hai điều đầu tiên:
typename
giữ tên của lớp giống tuple mà bạn đang tạo. Nó cần phải là một chuỗi.field_names
giữ tên của các trường hoặc thuộc tính của lớp giống tuple. Nó có thể là một chuỗi các chuỗi chẳng hạn như ["x", "y"]
hoặc một chuỗi đơn với mỗi tên được phân tách bằng khoảng trắng hoặc dấu phẩy, chẳng hạn như "x y"
hoặc "x, y"
.
Sử dụng một namedtuple
khi bạn cần
trả về nhiều giá trị có thể làm cho các hàm của bạn dễ đọc hơn đáng kể mà không cần quá nhiều nỗ lực. Hãy xem xét bản cập nhật sau về describe()
việc sử dụng namedtuple
giá trị trả về:
import statistics as st
from collections import namedtuple
def describe(sample):
Desc = namedtuple("Desc", ["mean", "median", "mode"])
return Desc(
st.mean(sample),
st.median(sample),
st.mode(sample),
)
Bên trong describe()
, bạn tạo một cuộc namedtuple
gọi Desc
. Đối tượng này có thể có các thuộc tính được đặt tên mà bạn có thể truy cập bằng cách sử dụng ký hiệu dấu chấm hoặc bằng cách sử dụng thao tác lập chỉ mục. Trong ví dụ này, những thuộc tính này
là "mean"
, "median"
, và "mode"
.
Bạn có thể tạo một Desc
đối tượng và sử dụng nó làm giá trị trả về. Để làm điều đó, bạn cần phải khởi tạo Desc
giống như cách bạn làm với bất kỳ lớp Python nào. Lưu ý rằng bạn cần cung cấp một giá trị cụ thể cho từng thuộc tính được đặt tên, giống như bạn đã làm trong return
câu lệnh của mình .
Đây là cách describe()
hoạt động bây giờ:
>>>
>>> sample = [10, 2, 4, 7, 9, 3, 9, 8, 6, 7]
>>> stat_desc = describe(sample)
>>> stat_desc
Desc(mean=5.7, median=6.0, mode=6)
>>> # Get the mean by its attribute name
>>> stat_desc.mean
5.7
>>> # Get the median by its index
>>> stat_desc[1]
6.0
>>> # Unpack the values into three variables
>>> mean, median, mode = describe(sample)
>>> mean
5.7
>>> mode
6
Khi bạn gọi describe()
với một mẫu dữ liệu số, bạn sẽ nhận được một namedtuple
đối tượng chứa giá trị trung bình, trung vị và chế độ của mẫu. Lưu ý rằng bạn có thể truy cập từng phần tử của bộ tuple bằng cách sử dụng ký hiệu dấu chấm hoặc thao tác lập chỉ mục.
Cuối cùng, bạn cũng có thể sử dụng thao tác giải nén có thể lặp lại để lưu trữ từng giá trị trong biến độc lập của riêng nó.
Chức năng quay lại: Đóng cửa
Trong Python, các hàm là các đối tượng hạng nhất . Đối tượng hạng nhất là một đối tượng có thể được gán cho một biến, được truyền làm đối số cho một hàm hoặc được sử dụng như một giá trị trả về trong một hàm. Vì vậy, bạn có thể sử dụng một đối tượng hàm làm giá trị trả về trong bất kỳ return
câu lệnh nào .
Một hàm nhận một hàm làm đối số, trả về một hàm dưới dạng kết quả hoặc cả hai là
một hàm bậc cao hơn . Hàm nhà máy đóng là một ví dụ phổ biến về hàm bậc cao trong Python. Loại hàm này nhận một số đối số và trả về một hàm bên trong . Hàm
bên trong thường được gọi là hàm đóng .
Một bao đóng mang thông tin về phạm vi thực thi bao quanh của nó. Điều này cung cấp một cách để giữ lại thông tin trạng thái giữa các lần gọi hàm. Các chức năng của nhà máy đóng cửa rất hữu ích khi bạn cần viết mã dựa trên khái niệm đánh giá lười biếng hoặc trì hoãn .
Giả sử bạn cần viết một hàm trợ giúp nhận một số và trả về kết quả của
phép nhân số đó với một thừa số đã cho. Bạn có thể viết mã chức năng đó như sau:
def by_factor(factor, number):
return factor * number
by_factor()
nhận factor
và number
làm đối số và trả về sản phẩm của họ. Vì factor
hiếm khi thay đổi trong ứng dụng của bạn, bạn cảm thấy khó chịu khi cung cấp cùng một hệ số trong mọi lệnh gọi hàm. Vì vậy, bạn cần một cách để giữ lại trạng thái hoặc giá trị của factor
giữa các lần gọi đến by_factor()
và chỉ thay đổi nó khi cần thiết. Để giữ lại giá trị
hiện tại factor
giữa các cuộc gọi, bạn có thể sử dụng một lệnh đóng.
Việc triển khai sau đây by_factor()
sử dụng một bao đóng để giữ lại giá trị của factor
giữa các cuộc gọi:
>>>
>>> def by_factor(factor):
... def multiply(number):
... return factor * number
... return multiply
...
>>> double = by_factor(2)
>>> double(3)
6
>>> double(4)
8
>>> triple = by_factor(3)
>>> triple(3)
9
>>> triple(4)
12
Bên trong by_factor()
, bạn xác định một hàm bên trong được gọi multiply()
và trả về nó mà không cần gọi nó. Đối tượng hàm mà
bạn trả về là một bao đóng lưu giữ thông tin về trạng thái của factor
. Nói cách khác, nó ghi nhớ giá trị của factor
giữa các cuộc gọi. Đó là lý do tại sao double
nhớ rằng factor
đã bằng 2
và triple
nhớ rằng factor
đã bằng 3
.
Lưu ý rằng bạn có thể tự do sử dụng lại double
và triple
vì họ không quên thông tin trạng thái tương ứng của họ.
Bạn cũng có thể sử dụng một lambda
hàm để tạo các bao đóng. Đôi khi việc sử dụng một lambda
hàm có thể làm cho nhà máy đóng cửa của bạn ngắn gọn hơn. Đây là cách triển khai thay thế của by_factor()
việc sử dụng một lambda
hàm:
>>>
>>> def by_factor(factor):
... return lambda number: factor * number
...
>>> double = by_factor(2)
>>> double(3)
6
>>> double(4)
8
Việc triển khai này hoạt động giống như ví dụ ban đầu. Trong trường hợp này, việc sử dụng một lambda
hàm cung cấp một cách nhanh chóng và
ngắn gọn để viết mã by_factor()
.
Chức năng Nhận và Trả hàng: Trang trí
Một cách khác để sử dụng return
câu lệnh để trả về các đối tượng hàm là viết các hàm trang trí . Một hàm decorator nhận một đối tượng hàm làm đối số và trả về một đối tượng hàm. Trình trang trí xử lý chức năng được trang trí theo một cách nào đó và trả về hoặc thay thế nó bằng một
chức năng hoặc đối tượng có thể gọi khác.
Trình trang trí rất hữu ích khi bạn cần thêm logic bổ sung vào các chức năng hiện có mà không cần sửa đổi chúng. Ví dụ: bạn có thể viết mã một trình trang trí để ghi nhật ký các lệnh gọi hàm, xác thực các đối số cho một hàm, đo thời gian thực thi của một hàm nhất định, v.v.
Ví dụ sau đây cho thấy một hàm decorator mà bạn có thể sử dụng để biết thời gian thực thi của một hàm Python nhất định:
>>>
>>> import time
>>> def my_timer(func):
... def _timer(*args, **kwargs):
... start = time.time()
... result = func(*args, **kwargs)
... end = time.time()
... print(f"Execution time: {end - start}")
... return result
... return _timer
...
>>> @my_timer
... def delayed_mean(sample):
... time.sleep(1)
... return sum(sample) / len(sample)
...
>>> delayed_mean([10, 2, 4, 7, 9, 3, 9, 8, 6, 7])
Execution time: 1.0011096000671387
6.5
Cú pháp @my_timer
phía trên tiêu đề của delayed_mean()
tương đương với biểu thức delayed_mean = my_timer(delayed_mean)
. Trong trường hợp này, bạn có thể nói rằng đó my_timer()
là trang trí delayed_mean()
.
Python chạy các hàm decorator ngay sau khi bạn nhập hoặc chạy một mô-đun hoặc một tập lệnh. Vì vậy, khi bạn gọi delayed_mean()
, bạn thực sự đang gọi giá trị trả về my_timer()
, là đối tượng
hàm _timer
. Lệnh gọi đến trang trí delayed_mean()
sẽ trả về giá trị trung bình của mẫu và cũng sẽ đo thời gian thực hiện của bản gốc delayed_mean()
.
Trong trường hợp này, bạn sử dụng time()
để đo thời gian thực hiện bên trong trình trang trí. time()
sống trong một mô-đun được gọi là mô-đun time
cung cấp
một tập hợp các chức năng liên quan đến thời gian. time()
trả về thời gian tính bằng giây kể từ kỷ nguyên dưới dạng số dấu phẩy động. Sự khác biệt giữa thời gian trước và sau khi gọi tới delayed_mean()
sẽ cho bạn biết về thời gian thực thi của hàm.
Ví dụ phổ biến khác của trang
trí bằng Python là classmethod()
,staticmethod()
, và property()
. Nếu bạn muốn tìm hiểu sâu hơn về trình trang trí Python, thì hãy xem phần Sơ đồ trang trí trên Python . Bạn cũng có thể kiểm tra Python Decorator 101 .
Trả lại các đối tượng do người dùng xác định: Mẫu ban đầu
Câu return
lệnh Python cũng có thể trả về các đối tượng do người dùng định nghĩa . Nói cách khác, bạn có thể sử dụng các đối tượng tùy chỉnh của riêng mình làm giá
trị trả về trong một hàm. Một trường hợp sử dụng phổ biến cho khả năng này là mô hình nhà máy .
Factory pattern xác định một giao diện để tạo các đối tượng một cách nhanh chóng nhằm đáp ứng các điều kiện mà bạn không thể đoán trước khi viết chương trình. Bạn có thể triển khai một nhà máy sản xuất các đối tượng do người dùng xác định bằng cách sử dụng một hàm nhận một số đối
số khởi tạo và trả về các đối tượng khác nhau theo đầu vào cụ thể.
Giả sử bạn đang viết một ứng dụng vẽ tranh. Bạn cần tạo nhanh các hình dạng khác nhau để đáp ứng các lựa chọn của người dùng. Chương trình của bạn sẽ có hình vuông, hình tròn, hình chữ nhật, v.v. Để tạo những hình dạng đó một cách nhanh chóng, trước tiên bạn cần tạo các lớp hình dạng mà bạn sẽ sử dụng:
class Circle:
def __init__(self, radius):
self.radius = radius
# Class implementation...
class Square:
def __init__(self, side):
self.side = side
# Class implementation...
Khi bạn đã có một lớp cho mỗi hình dạng, bạn có thể viết một hàm lấy tên của hình
dạng dưới dạng chuỗi và danh sách tùy chọn các đối số ( *args
) và các đối số từ khóa ( **kwargs
) để tạo và khởi tạo các hình dạng một cách nhanh chóng:
def shape_factory(shape_name, *args, **kwargs):
shapes = {"circle": Circle, "square": Square}
return shapes[shape_name](*args, **kwargs)
Hàm này tạo một thể hiện của hình dạng cụ thể và trả về nó cho người gọi. Giờ đây, bạn có thể sử dụng shape_factory()
để tạo các đối tượng có hình dạng khác nhau theo nhu cầu của người dùng:
>>>
>>> circle = shape_factory("circle", radius=20)
>>> type(circle)
<class '__main__.Circle'>
>>> circle.radius
20
>>> square = shape_factory("square", side=10)
>>> type(square)
<class '__main__.Square'>
>>> square.side
10
Nếu bạn gọi shape_factory()
với tên của hình dạng cần thiết dưới dạng chuỗi, thì bạn sẽ nhận được một phiên bản mới của hình dạng phù hợp với hình dạng shape_name
bạn vừa chuyển đến nhà máy.
Sử dụng returntrong try… finallyKhối
Khi bạn sử dụng một return
câu lệnh bên trong một try
câu lệnh với một finally
mệnh đề, finally
mệnh đề đó luôn được thực thi trước return
câu lệnh. Điều này đảm bảo rằng mã trong finally
mệnh đề sẽ luôn chạy. Kiểm tra ví dụ sau:
>>>
>>> def func(value):
... try:
... return float(value)
... except ValueError:
... return str(value)
... finally:
... print("Run this before returning")
...
>>> func(9)
Run this before returning
9.0
>>> func("one")
Run this before returning
'one'
Khi bạn gọi func()
, bạn sẽ được value
chuyển đổi thành một số dấu phẩy động hoặc một đối tượng chuỗi. Trước khi làm điều đó, hàm của bạn chạy finally
mệnh đề và in một thông báo ra màn hình của bạn. Bất kỳ mã nào bạn thêm vào finally
mệnh đề sẽ được thực thi trước khi hàm chạy return
câu lệnh của nó .
Sử dụng returntrong các chức năng của máy phát điện
Một hàm Python với một yield
câu lệnh trong phần thân của nó là một hàm trình tạo . Khi bạn gọi một hàm trình tạo, nó sẽ trả về một trình lặp của trình tạo . Vì vậy, bạn có thể
nói rằng một chức năng máy phát điện là một nhà máy sản xuất máy phát điện .
Bạn có thể sử dụng một return
câu lệnh bên trong một hàm của trình tạo để chỉ ra rằng trình tạo đã được thực hiện. Câu return
lệnh sẽ làm cho máy phát điện tăng lên a StopIteration
. Giá trị trả về sẽ được chuyển làm đối số cho bộ khởi tạo của StopIteration
và sẽ được gán
cho .value
thuộc tính của nó .
Đây là một trình tạo sản lượng 1
và 2
theo yêu cầu và sau đó trả về 3
:
>>>
>>> def gen():
... yield 1
... yield 2
... return 3
...
>>> g = gen()
>>> g
<generator object gen at 0x7f4ff4853c10>
>>> next(g)
1
>>> next(g)
2
>>> next(g)
Traceback (most recent call last):
File "<input>", line 1, in <module>
next(g)
StopIteration: 3
gen()
trả về một đối tượng trình tạo sản lượng 1
và 2
theo yêu cầu. Để truy xuất từng số của đối tượng trình tạo, bạn có thể sử dụng next()
, đây là một hàm tích hợp
để truy xuất mục tiếp theo từ trình tạo Python.
Hai cuộc gọi đầu tiên để next()
truy xuất 1
và 2
, tương ứng. Trong cuộc gọi thứ ba, máy phát điện bị cạn kiệt và bạn nhận được một StopIteration
. Lưu ý rằng giá trị trả về của hàm tạo ( 3
) trở thành .value
thuộc tính của StopIteration
đối tượng.
Phần kết luận
Câu return
lệnh Python cho phép bạn gửi bất kỳ đối tượng Python nào từ các hàm tùy chỉnh của bạn trở lại mã người gọi. Câu lệnh này là một phần cơ bản của bất kỳ hàm hoặc phương thức Python nào. Nếu bạn thành thạo cách sử dụng nó, thì bạn sẽ sẵn sàng viết mã các hàm mạnh mẽ.
Trong hướng dẫn này, bạn đã học cách:
- Sử dụng hiệu quả câu lệnh Python
return
trong các hàm của bạn - Trả lại một hoặc nhiều
giá trị từ các hàm của bạn cho mã người gọi
- Áp dụng các phương pháp hay nhất khi sử dụng
return
câu lệnh
Ngoài ra, bạn đã học được một số trường hợp sử dụng nâng cao hơn cho return
câu lệnh, chẳng hạn như cách viết mã một hàm nhà máy đóng và một hàm trang trí . Với kiến thức này, bạn sẽ có thể viết nhiều hàm Pythonic , mạnh mẽ và dễ bảo trì hơn bằng Python.