Python: Hàm (Function)

Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực

Giới thiệu

Hầu hết các chương trình viết bằng ngôn ngữ Python (cũng như viết bằng những ngôn ngữ khác như C, C++, Java, C#) thường được phân nhỏ thành các hàm (function). Hàm (Function) là phần kiến thức rất quan trọng trong Python, nắm tốt kiến thức về hàm bạn sẽ có được thuận lợi khi tìm hiểu các ngôn ngữ lập trình khác cũng như phần kiến thức về lập trình hướng đối tượng.

Khái niệm

Hàm là đoạn chương trình thực hiện trọn vẹn một công việc nhất định (cụ thể). Hàm giúp chia cắt việc lớn thành nhiều việc nhỏ hơn, điều này tương đương với việc chia bài toán lớn thành các bài toán nhỏ hơn để giải, như vậy thì việc giải bài toán sẽ trở nên dễ dàng hơn. Ngoài ra, hàm còn giúp cho chương trình trở nên sáng sủa, dễ sửa, nhất là đối với các chương trình lớn.

Định nghĩa một hàm

Để định nghĩa hay tạo một hàm, ta có thể sử dụng cú pháp như sau:

def tên_hàm(danh_sách_tham_số) {
  khai_báo_các_biến_của_hàm;  //Nếu cần
  thân_hàm;
  [return giá_trị;]
}

, trong đó:

+ tên_hàm buộc phải có và việc đặt tên phải tuân theo quy tắc đặt tên. Ví dụ muốn sử dụng tên hàm là "Tinh binh phuong" thì không được vì có dấu cách trong đó, ta có thể thay bằng "Tinh_binh_phuong" hoặc "tinhBinhPhuong".

+ danh_sách_tham_số: Không bắt buộc, nghĩa là danh_sách_tham_số có thể có hoặc không. Trong trường hợp hàm cần dữ liệu để xử lý thì cần phải có danh_sách_tham_số, ngược lại thì không cần.

+ Cặp () bao ngoài danh_sách_tham_số và sau tên_hàm là bắt buộc phải có, ngay cả không có danh_sách_tham_số.

+ Dấu : là bắt buộc phải có đối với mọi định nghĩa hàm.

+ [return giá_trị;]: Lệnh này dùng để trả về giá trị cho hàm, giá_trị sẽ được trả về nơi hàm được gọi. Nếu ta không cần hàm trả về giá_trị thì không có lệnh này. giá_trị có thể là một hằng, giá trị của biến, giá trị của biểu thức hoặc giá trị trả về từ một lời gọi hàm khác.

Một số chú ý đối với hàm:

- Có thể thể gọi một hàm từ hàm khác nhưng bạn không nên định nghĩa hàm bên trong hàm nếu không cần thiết.

- Bạn có thể định nghĩa một hàm nằm trên hoặc nằm dưới hàm khác.

- Mỗi hàm chỉ có thể trả về được duy nhất một giá trị.

- Nơi mà hàm trả về giá trị chính là nơi mà nó được gọi.

Sau đây là một ví dụ áp dụng hàm, đó là viết chương trình tính giai thừa: S = n! (=1*2*3*4*…*n). Chương trình được viết như sau:

def nhapN():
  return int(input("Mi nhp 1 s nguyên: "))

def giaiThua(n): #đnh nghĩa hàm tiaiThua()
  KQ=1.0; #đây là biến cc b  for i in range(1,n+1): #n đây là tham sca hàm giaiThua()
    KQ = KQ*i;
  return KQ; #hàm trvgiá trlưu trong biến KQ ti nơi gi

def main():  #đnh nghĩa hàm main() đchy chương trình

  n=nhapN(); #khai báo biến n và gi ti hàm nhapN()

  print("Giai tha ca",n,"là",giaiThua(n)); #gi đến hàm giaiThua() trong đó truyn giá trca n (thuc hàm main()) cho đi sn ca hàm giaiThua()

main() #gi đến (kích hot, yêu cu) hàm main()

Dưới đây là một kết quả thực hiện chương trình trên:

Giai thừa 

Lời gọi hàm

Các hàm thường giao tiếp hay gọi đến nhau bằng lời gọi hàm (call function). Việc giao tiếp hay gọi đến nhau của các hàm có thể thông qua việc truyền đối số.

Cú pháp gọi hàm:

tên_hàm(danh_sách_đối_số)

trong đó,

danh_sách_đối_số: Nếu phần định nghĩa hàm có danh_sách_tham_số thì bắt buộc phải có danh_sách_đối_số với số lượng phải bằng với số lượng tham số trong danh_sách_tham_số. Bản chất của danh_sách_đối_số là các dữ liệu (giá trị) muốn truyền đi.

Ví dụ:

nh nghĩa hàm
def tinhTong(a, b):
  print(a,"+",b,"=",a+b)

tinhTong(1, 2)    #li gi hp l, 1 được truyn cho a, 2 được truyn cho b
tinhTong(1)       #li gi không hp l, 1 được truyn cho a, nhưng không có dliu truyn cho b
tinhTong(1, 2, 3) #li gi không hp lvì slượng dliu truyn đi vượt quá slượng tham snhn

Truyền dữ liệu

Tất cả các đối số trong ngôn ngữ Python đều được truyền bằng tham chiếu. Điều này có nghĩa là ta có thể thay đổi giá trị của biến bên trong hàm gọi tại hàm được gọi.

Ví dụ:

nh nghĩa hàm swap()
def swap(myList):
  tg=myList[0]
  myList[0]=myList[1]
  myList[1]=tg
  
#đnh nghĩa hàm main()
def main():
  a=5
  b=10
  myList=[a,b];

  swap(myList) #gi ti hàm swap truyn đi list
  a=myList[0]
  b=myList[1]
  print(a,b)   #in ra kết qusau khi swap, a=10 và b=5
main()

Tuy nhiên, có một ví dụ sau đây trong đó đối số được truyền bằng tham chiếu nhưng tham chiếu lại bị ghi đè bên trong hàm được gọi, và điều này dẫn đến dữ liệu trong myList không thay đổi được ở hàm gọi.

Ví dụ:

def swap(myList):
  myList=[9,10]

def main():
  a=5
  b=10
  myList=[a,b];

  swap(myList)
  a=myList[0]
  b=myList[1]
  print(a,b) #a vn cha 5, b vn cha 10
main()

Trong ví dụ trên, trong hàm swap() thì tham số myList là cục bộ, và điều này dẫn đến việc mặc dù thay đổi myList bên trong hàm nhưng không ảnh hưởng đến myList trong hàm main(). Kết quả là không hoán đổi giá trị của hai biến a và b cho nhau được.

Đối số hàm

Bạn có thể gọi một hàm bằng cách sử dụng các loại đối số sau:

  • Đối số bắt buộc
  • Đối số từ khóa
  • Đối số mặc định
  • Đối số có độ dài thay đổi

Đối số bắt buộc

Các đối số bắt buộc là các đối số được truyền cho một hàm theo đúng thứ tự vị trí của các tham số tại phần định nghĩa hàm. Ở đây, số lượng đối số trong lệnh gọi hàm phải khớp chính xác với định nghĩa hàm.

Trong ví dụ sau, để gọi hàm printme() thì ta cần phải truyền một đối số, nếu không nó sẽ gây ra lỗi cú pháp:

# đnh nghĩa hàm có mt tham sdef printMe(str):
  "in ra ni dung trong tham s str"
  print(str)
  return;

# nhưng li gi hàm như sau là sai vì không có đi sprintMe()

Khi đoạn mã trên được thực thi, nó tạo ra kết quả sau:

Traceback (most recent call last):
  File "E:/Courses/Python/demo.py", line 8, in <module>
    printMe()
TypeError: printMe() missing 1 required positional argument: 'str'

Đối số từ khóa

Các đối số từ khóa có liên quan đến các lệnh gọi hàm. Khi bạn sử dụng các đối số từ khóa trong một lệnh gọi hàm thì trình gọi sẽ xác định các đối số thông qua tên tham số.

Điều này cho phép ta bỏ qua các đối số hoặc đặt chúng không theo thứ tự vì trình thông dịch Python có thể sử dụng các từ khóa được cung cấp để đối sánh các giá trị với các tham số. Bạn cũng có thể truyền từ khóa đến hàm printMe() theo cách sau:

# đnh nghĩa hàm
def printMe(str):
  print(str)
  return;

# gi hàm printMe()
printMe(str = "I love Python")

Khi đoạn mã trên được thực thi, nó tạo ra kết quả sau:

I love Python

Ví dụ sau đây bạn sẽ thấy sự rõ ràng hơn về đối số từ khóa, trong đó thứ tự của các tham số không còn quan trọng.

def printInfo(name, age):
  print("Name:", name)
  print("Age:", age)
  return;

printInfo(age=5, name="V1Study")

Khi đoạn mã trên được thực thi, nó tạo ra kết quả sau:

Name: V1Study
Age: 5

Tham số mặc định

Đối số mặc định là đối số giả định giá trị mặc định nếu giá trị không được cung cấp trong lệnh gọi hàm cho đối số đó. Ví dụ sau đây đưa ra một ý tưởng về các đối số mặc định, trong đó sẽ in ra giá trị mặc định của tham số age nếu nó không nhận được dữ liệu:

def printInfo(name, age = 6):
  print("Name:", name)
  print("Age:", age)
  return;

printInfo(age=5, name="V1Study")
printInfo(name="V1Study Academy") #thiếu đi snhưng không bli

Khi đoạn mã trên được thực thi, nó tạo ra kết quả sau:

Name: V1Study
Age: 5
Name: V1Study Academy
Age: 6

Tham số có độ dài thay đổi

Trong trường hợp định nghĩa một hàm cần nhận nhiều dữ liệu thì tức là ta cần khai báo nhiều tham số, lúc này ta sẽ thấy sự bất tiện vì mất khá nhiều thời gian. Python đưa ra giải pháp là sử dụng tham số có độ dài thay đổi. Các tham số này được gọi là tham số có độ dài thay đổi và không có tên trong định nghĩa hàm, không giống như các tham số bắt buộc và mặc định.

Cú pháp cho định nghĩa hàm với các tham số không phải từ khóa là như sau:

def functionname([formal_args,] *var_args_tuple ):
   "function_docstring"
   function_suite
   return [expression]

Dấu hoa thị (*) được đặt trước tên biến sẽ mang nghĩa nhận các giá trị của tất cả các đối số không có từ khóa. Bộ giá trị này được phép trống (tức không chứa giá trị nào). Sau đây là một ví dụ đơn giản

nh nghĩa hàm vi yêu cu phi nhn được ít nht mt dliu:
#arg1 là tham sbt buc phi nhn được dliu
#*vartuple là tham skhông bt buc phi nhn được dliu
def printInfo(arg1, *vartuple):
  print("arg1:")
  print(arg1)
  print("Other parameters:")
  for var in vartuple:
  print(var)
  print("=============")
  return;

printInfo(10)
printInfo(20, 30, 40)

Khi đoạn mã trên được thực thi, nó tạo ra kết quả sau:

arg1:
10
Other parameters:
=============
arg1:
20
Other parameters:
30
40
=============

Hàm ẩn danh

Các hàm này được gọi là ẩn danh vì chúng không được khai báo theo cách chuẩn, không sử dụng từ khóa def .

Để tạo hàm ẩn danh ta sử dụng từ khóa lambda:

  • Các biểu mẫu Lambda có thể nhận bất kỳ số lượng đối số nào nhưng chỉ trả về một giá trị dưới dạng một biểu thức. Chúng không thể chứa lệnh hoặc nhiều biểu thức.
  • Một hàm ẩn danh không thể là một lệnh gọi trực tiếp để in vì Lambda yêu cầu một biểu thức.
  • Các hàm Lambda có không gian tên cục bộ riêng của chúng và không thể truy cập các biến khác với các biến trong danh sách tham số của chúng và các biến trong không gian tên chung.
  • Mặc dù có vẻ như Lambda là phiên bản một dòng của một hàm, nhưng nó không tương đương với các câu lệnh nội tuyến trong C hay C++, có mục đích là chuyển cấp phát ngăn xếp hàm trong khi gọi vì lý do hiệu suất.

Cú pháp

Cú pháp của hàm lambda chỉ chứa một câu lệnh duy nhất, như sau:

lambda [arg1 [,arg2,.....argn]]: biểu_thức

Sau đây là ví dụ cho thấy cách hoạt động của dạng hàm lambda:

# đnh nghĩa hàm n danh
sum = lambda arg1, arg2: arg1 + arg2;

# gi hàm n danh
print("Sum =", sum(10, 20))
print("Sum =", sum(20, 20))

Khi đoạn mã trên được thực thi, nó tạo ra kết quả sau:

Sum = 30
Sum = 40

Câu lệnh return

Câu lệnh return [giá_trị] sẽ thoát khỏi hàm, trả về giá_trị cho nơi gọi. Nếu hàm không trả về giá trị thì ta không dùng câu lệnh này hoặc chỉ viết đơn giản là return.

Tất cả các ví dụ ta thấy ở trên không trả về bất kỳ giá trị nào. Bạn có thể trả về một giá trị từ một hàm như ví dụ sau:

def tinhTong(number1, number2):
  total = number1 + number2
  return total;#trvnơi gi hàm tng tính được

n1 = 10
n2 = 20
print(n1,"+",n2,"=",tinhTong(n1,n2))

Khi đoạn mã trên được thực thi, nó tạo ra kết quả sau:

10 + 20 = 30

Phạm vi của các biến

Tất cả các biến trong một chương trình có thể không truy cập được ở tất cả các vị trí trong chương trình đó. Điều này phụ thuộc vào nơi bạn đã khai báo các biến.

Phạm vi của một biến xác định phần của chương trình mà bạn có thể truy cập vào một số biến cụ thể. Có hai phạm vi cơ bản của các biến trong Python:

  • Biến toàn cục (global)
  • Biến cục bộ (local)

Biến toàn cục so với biến cục bộ

Các biến được tạo bên trong thân hàm có phạm vi cục bộ và các biến được tạo bên ngoài tất cả các hàm có phạm vi toàn cục.

Điều này có nghĩa là các biến cục bộ chỉ có thể được truy cập bên trong hàm mà chúng được tạo, trong khi các biến toàn cục có thể được truy cập trong toàn bộ phần thân chương trình bởi tất cả các hàm. Khi bạn gọi một hàm, các biến được khai báo bên trong nó sẽ được đưa vào phạm vi. Sau đây là một ví dụ đơn giản:

total = 0; #đây là biến global vì nm ngoài tt ccác hàm

def sum(arg1, arg2):
  total = arg1 + arg2 #đây là biến local
  print("Biến total trong hàm sum():", total)
  return total

sum(10, 20)
print("Biến total ngoài các hàm:", total)

Khi đoạn mã trên được thực thi, nó tạo ra kết quả sau:

Biến total trong hàm sum(): 30
Biến total ngoài các hàm: 0
» Tiếp: Lời gọi hàm (Call the function)
« Trước: Vòng lặp while
Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực
Copied !!!