LẬP TRÌNH PYTHON
Hàm trong Python, cách xây dựng Hàm
ĐỊNH NGHĨA HÀM TRONG PYTHON
Function in Python
Hàm là một đoạn chương trình bao gồm một hoặc nhiều xử lý nhằm giải quyết một công việc nào đó và được xây dựng với mục đích TÁI SỬ DỤNG. Hàm có thể được xây dựng bởi lập trình viên hoặc hàm có sẵn trong ngôn ngữ lập trình (built-in functions). Ví dụ trong Python, chúng ta có thể liệt kê các hàm có sẵn như printt, max, min, pow, sqrt,…
Structure of Functions
Trong nội dung này, chúng ta sẽ tìm hiểu:
Cách định nghĩa và gọi hàm trong Python
Ý nghĩa của thụt lề (khoảng trắng) trong Python
Hàm trả về giá trị như thế nào?
Đối số trong hàm
Cách định nghĩa và gọi hàm trong Python
Hàm trong Python được định nghĩa bởi câu lệnh “def” theo sau là tên hàm và dấu ngoặc đơn
Thí dụ:
Cùng khai báo một hàm bằng cách sử dụng lệnh “def func1 ():” và gọi hàm. Đầu ra của hàm sẽ là “I am learning Python function”.
Xây dựng hàm
Lời gọi func1() sẽ gọi tới hàm func1() ta đã định nghĩa và in ra dòng chữ: “I am learning Python function”
Việc định nghĩa một hàm trong Python cũng có một số quy tắc
Bất kỳ đối số hoặc tham số đầu vào nào cũng cần được đặt trong dấu ngoặc đơn.
Câu lệnh đầu tiên của hàm có thể là một câu lệnh tùy chọn – docstring hay chuỗi tài liệu của hàm.
Mã nguồn trong mỗi hàm bắt đầu sau dấu hai chấm (:) và phải được thụt lề (dùng khoảng trắng)
Câu lệnh return(biểu thức) sẽ khiến chương trình thoát ra khỏi hàm, nó trả về một giá trị cho người gọi. Câu lệnh return không có đối số tương tự với việc gọi return None.
Ý nghĩa của thụt lề (khoảng trắng) trong Python
Trước khi tìm hiểu sâu hơn về hàm, chúng ta cần nắm được quy tắc thụt lề để khai báo hàm trong Python. Các quy tắc này cũng được áp dụng cho các đối tượng khác của Python như khai báo điều kiện, vòng lặp hoặc biến.
Python tuân theo một kiểu thụt lề nhất định để xác định đâu là mã nguồn, vì các hàm trong Python không được khai báo theo cách sử dụng dấu ngoặc nhọn để xác định nơi bắt đầu và kết thúc của hàm, vì vậy Python cần dựa vào quy tắc thụt lề. Ở đây chúng ta lấy một ví dụ đơn giản với lệnh “print”. Khi chúng ta viết hàm “print” ngay bên dưới def func1(): Chương trình sẽ báo lỗi: “indentation error: expected an indented block”.
Indentation in Python
Bây giờ, khi bạn thêm thụt lề (dấu cách) phía trước hàm “print”, chương trình sẽ in ra kết quả như mong đợi.
Indentation in correct
Hàm trả về giá trị như thế nào?
Lệnh return trong Python chỉ định giá trị nào sẽ trả lại cho lời gọi hàm.
Cùng tìm hiểu thông qua ví dụ sau:
- Bước 1) Ở đây - chúng ta sẽ thấy hàm không có câu lệnh “trả về”. Ví dụ: chúng ta muốn tính bình phương của “4”, chương trình sẽ in ra “16”. Chúng ta cũng có thể làm được điều này với câu lệnh đơn giản print(x*x), nhưng khi bạn gọi hàm “print square”, chương trình sẽ trả về “None”. Điều này là do khi bạn gọi hàm, không có giá trị trả về và hàm kết thúc. Python trả về “None” khi rời khỏi hàm.
Step 1
- Bước 2) Để quan sát rõ hơn, chúng ta thay thế lệnh print bằng phép gán.
Step 2
Khi bạn chạy lệnh “print square(4)”, chương trình sẽ in ra kết quả trả về của hàm square, nhưng do chúng ta không khai báo giá trị trả về trong hàm nên kết quả in ra là “None”.
Bước 3) Giờ chúng ta sẽ tìm cách thu được kết quả thông qua lệnh “return”. Khi bạn gọi hàm “return” và thực thi mã nguồn, chương trình sẽ trả về giá trị “16”.
Bước 4) Bản thân các hàm trong Python chính là một đối tượng, và một đối tượng sẽ có giá trị nhất định. Ở đây chúng ta có thể thấy cách Python tương tác với các đối tượng. Khi bạn chạy lệnh “print square”, nó sẽ trả về giá trị của đối tượng. Vì chúng ta không truyền vào bất kỳ đối số nào, chúng ta cũng không có hàm cụ thể nào để chạy nên chương trình trả về một giá trị mặc định (0x021B2D30), giá trị này chính là địa chỉ của đối tượng.
Step 4
Đối số trong hàm
Đối số là một giá trị được truyền cho hàm khi nó được gọi.
Nói cách khác, ở phía người gọi, nó là đối số, còn ở phía hàm, nó được coi là tham số.
Cùng tìm hiểu cách đối số hoạt động trong Python:
Bước 1) Các đối số được khai báo trong định nghĩa hàm. Khi gọi hàm, bạn có thể truyền các giá trị cho đối số đó như hình bên dưới
Attributes in Python
Bước 2) Để khai báo giá trị mặc định của đối số, ta cầm gán cho nó một giá trị trong định nghĩa hàm.
Default attribute
The result
Chúng ta có thể truyền vào nhiều đối số thông qua một mảng. Trong ví dụ dưới, chúng ta sẽ truyền vào nhiều đối số (1,2,3,4,5) bằng cách gọi hàm với (* args).
Ví dụ: Khai báo nhiều đối số dưới dạng (1,2,3,4,5) khi chúng ta gọi hàm với (* args); chương trình sẽ in ra kết quả là (1,2,3,4,5).
Array of attributes
Docstring
Chuỗi đầu tiên ngay sau tiêu đề hàm được gọi là docstring (documentation string). Nó dùng để giải thích chức năng cho hàm. Docstring không bắt buộc nhưng dùng nó để giải thích ngắn gọn về chức năng của hàm sẽ giúp người dùng có thể hiểu hàm ngay mà không cần tìm định nghĩ để xem.
Để in ra nội dung của một hàm nào đó, sử dụng cú pháp
#print(sum.__doc__)
Biến và phạm vi của Biến trong Python
Variables and Scope
Trong Python, một biến được khai báo bên ngoài hàm hoặc trong phạm vi toàn cục được gọi là biến toàn cục. Điều này có nghĩa, biến toàn cục có thể được truy cập bên trong hoặc bên ngoài hàm.
Biến toàn cục có tên gọi là Global Variable.
= "Học lập trình Python"
var1
def fun1():
print("Trong hàm:", var1)
fun1()
## Trong hàm: Học lập trình Python
print("Ngoài hàm:", var1)
## Ngoài hàm: Học lập trình Python
Khi sử dụng biến toàn cục bên trong một hàm thì chúng ta không được thay đổi giá trị cho biến. Như ví dụ dưới đây là sai, bởi vì chúng ta đã thay đổi giá trị cho biến x.
#x = 10
#def fun2():
#x = x * 2
#print(x)
#fun2()
Lý do : Vì bên trong hàm fun2 chúng ta đã thay đổi giá trị cho biến x nên mặc nhiên nó sẽ hiểu biến x là biến cục bộ, mà biến cục bộ thì đoạn code x= x* 2 sẽ sai vì biến x chưa được khai báo.
Để giải quyết vấn đề này thì chúng ta sử dụng thêm từ khóa global, Python cung cấp từ khóa global, khi khai báo từ khóa này cho một biến bất kì thì trình biên dịch sẽ tự hiểu biến đó sẽ trỏ tới một biến cục bộ nằm bên ngoài hàm.
= 10
x
def fun2():
global x
= x * 2
x print(x)
# In giá trị 20
fun2()
# In giá trị 20 vì x = 20
## 20
print(x)
## 20
Trong Python, biến cục bộ được hiểu là biến được khai báo bên trong hàm hoặc phạm vi cục bộ, những biến này gọi là biến cục bộ.
Biến cục bộ có tên gọi là Local Variable.
= "global"
x
def foo():
global x
= "local"
y = x * 2
x print(x)
print(y)
foo()
## globalglobal
## local
= 5
x
def plusNum():
global x
= x * 2
x
plusNum()print(x) # kết quả là 10
## 10
plusNum()print(x) # kết quả là 20
## 20
plusNum()print(x) # kết quả là 40
## 40
Recursion 2
Hàm Đệ Quy - Recursion
Đệ quy trong Python hay còn gọi là recursion python. Nói về toán học thì đệ quy là thuật toán giải quyết bài toán bằng cách gọi lại chính thuật toán đó, thao tác này sẽ thực hiện liên tục cho đến khi gặp điều kiện dừng.
Điều kiện dừng: Một hàm đệ quy cần phải có điều kiện dừng đề dừng việc tự gọi lại nó. Hàm đệ quy chấm dứt khi mỗi lần gọi đệ quy thì số giải pháp của vấn đề được giảm bớt và tiến gần đến điều kiện cơ sở. Một điều kiện cơ sở là điểm mà ở đó vấn đề được giải quyết và không cần đệ quy thêm. Nếu các lần gọi đệ quy không thể đến được điều kiện cơ sở thì hàm đệ quy trở thành một vòng lặp vô hạn. Như vậy, có thể nói đệ quy trong khoa học máy tính là một phương pháp giải quyết vấn đề dựa trên việc giải quyết các trường hợp nhỏ hơn của cùng vấn đề đó.
Trong Python, hàm đệ quy cũng tương tự như vậy, có thể gọi đến chính nó và có một điều kiện cơ sở để chấm dứt đệ quy.
Ví dụ về hàm đệ quy trong Python
Ví dụ kinh điển nhất về hàm đệ quy chính là tính giai thừa của một số nguyên. Giai thừa của một số là kết quả của phép nhân từ một đến số đó. Ví dụ, 5 giai thừa (được viết là 5!)
Trong Python, hàm tính giai thừa của một số được viết như sau:
# Code by Trần Quang Quý
def giaithua(n):
"""Xây dựng hàm đệ quy tính giai thừa"""
if n == 1:
return 1
else:
return (n * giaithua(n-1))
= 5
num print("Giai thừa của", num, "là", giaithua(num))
## Giai thừa của 5 là 120
Khi gọi hàm giaithua(n) với số nguyên dương, nó sẽ gọi đệ quy bằng cách giảm dần số. Mỗi một lần gọi hàm, nó sẽ nhân số với giai thừa của 1 cho đến khi số bằng 1. Việc gọi đệ quy này có thể được giải thích trong các bước sau:
Phép đệ quy trên kết thúc khi số giảm xuống đến 1. Đây được gọi là điều kiện cơ sở. Mỗi hàm đệ quy phải có một điều kiện cơ sở để dừng việc đệ quy nếu không nó sẽ trở thành hàm vô hạn, tự gọi mãi đến nó.
Ưu điểm của hàm đệ quy:
Các hàm đệ quy làm cho code trông gọn gàng và nhẹ nhàng hơn.
Những nhiệm vụ phức tạp có thể được chia thành những vấn đề đơn giản hơn bằng cách sử dụng đệ quy.
Tạo trình tự với đệ quy dễ dàng hơn so với việc sử dụng những vòng lặp lồng nhau.
Nhược điểm của đệ quy:
Đôi khi logic đằng sau đệ quy khá khó để hiểu rõ.
Gọi đệ quy tốn kém (không hiệu quả) vì chúng chiếm nhiều bộ nhớ và thời gian.
Các hàm đệ quy rất khó để gỡ lỗi.
Mỗi một lần hàm đệ quy tự gọi nó sẽ lưu trữ trên bộ nhớ. Vì thế, nó tiêu tốn nhiều bộ nhớ hơn so với hàm truyền thống. Python sẽ dừng gọi hàm sau 1000 lần gọi. Nếu bạn chạy code dưới đây:
Limitation of recursion
Có thể giải quyết vấn đề này bằng cách điều chỉnh số lần gọi đệ quy, như sau:
import sys
5000)
sys.setrecursionlimit(
def giaithua(n):
if n == 1:
return 1
else:
return (n * giaithua(n-1))
print (giaithua(1001))
## 402789647337170867317246136356926989705094239074925347176343710340368450911027649612636252695456374205280468598807393254690298539867803367460225153499614535588421928591160833678742451354915921252299285456946271396995850437959540645019696372741142787347450281325324373824456300226871609431497826989489109522725791691167945698509282421538632966523376679891823696900982075223188279465194065489111498586522997573307838057934994706212934291477882221464914058745808179795130018969175605739824237247684512790169648013778158661520384916357285547219660337504067910087936301580874662367543921288988208261944834178369169805682489420504038334529389177845089679546075023305854006141256288633820079940395329251563788399404652902154519302928365169452383531030755684578503851488154092323576150311569325891190105926118761607100286827930472944913272420825078912158741589850136017030887975452922434889688775883386977825215904423682478943313806072144097432418695807412571292308739802481089407002523955080148184062810447564594783139830113821372260474145316521647368313934670783858482781506915288378941348078689691815657785305896912277993200639858696294199549107738635599538328374931258525869323348477334798827676297868823693023377418942304272267800509765805435653787530370118261219994752588866451072715583785495394684524593296728611334955079882857173250037068541860372512693170819259309411027837176612444692649174536429745421086287708588130082168792750697158901737130221751430550976429258055277255676893874108456870904122902259417224707137723406125811549952159629766771063079472679280213882978523785424760309678138268708239764925768714349554665438389311198715040908077757086900159389712443987670244241787904585093011546861502058550090914877900852701619648229332192401075747543562989953271508977501771085759521631427816116191761031257454497039673414248149210836002497114107565960458576525212556159634975715552638678172137468172843066451093984443636560722213668172225585711566558134467392654185460222589723312097599987253417831473939565071006344352518096564427781204200068323913056897090916602712260306869786107237077572445866572945760977721639408338430009976028970539150822336553856613962747814621747092348996915755983464741082000337526945990059365493439921937093368896754791416759604324895514660325913157843796039917819613717350380997781225472000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Khử đệ quy
Thực tế thì sử dụng đệ quy sẽ tốn rất nhiều tài nguyên của máy tính, bởi nó sẽ phải lưu trữ khá nhiều thông tin để tạo ra một biểu thức cuối cùng. Vì vậy người ta thưởng sử dụng khử đệ quy để chuyển đổi từ đệ quy thành vòng lặp.
Như bài toán toán dãy Fibonacci ta có thể sử dụng trong vòng lặp rất dễ dàng và nhanh chóng.
= 4
num = 1;
result for i in range(1, num + 1):
= result * i
result
print("Kết quả khử đệ quy là: ", result)
## Kết quả khử đệ quy là: 24
Bài tập áp dụng xây dựng hàm:
Bài tập 1: Viết chương trình xây dựng hàm kiểm tra số nguyên tố.
Bài tập 2: Viết chương trình kiểm tra số hoàn hảo.
Bài tập 3: Viết hàm đệ quy trong Python để tính tổng S=1+2+3+…+n
Bài tập 4: Viết hàm tìm ra số lớn nhất trong ba số a, b, c nhập vào từ bàn phím.
Bài tập 5: Viết hàm tính tổng các số nguyên tố trong khoảng [0,1000].
Bài tập 6: Viết chương trình hiển thị giá trị tuyệt đối của một số nguyên nhập vào từ bàn phím
Bài tập 7: Viết hàm tính giai thừa của một số nguyên dương.
Fibonacci1
Bài tập 8: Hãy viết chương trình tìm n số Fibonacci đầu tiên.
Quy luật của dãy số Fibonacci: số tiếp theo bằng tổng của 2 số trước, 2 số đầu tiên của dãy số là 0, 1. Ví dụ: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, …
Có 2 cách để viết chương trình dãy số Fibonacci trong python
Tính dãy số Fibonacci trong python không dùng phương pháp đệ quy
Tính dãy số Fibonacci trong python sử dụng phương pháp đệ quy
def fibonacci(n):
= 0;
f0 = 1;
f1 = 1;
fn
if (n < 0):
return -1;
elif (n == 0 or n == 1):
return n;
else:
for i in range(2, n):
= f1;
f0 = fn;
f1 = f0 + f1;
fn return fn;
print("10 số đầu tiên của dãy số fibonacci: ");
## 10 số đầu tiên của dãy số fibonacci:
= "";
sb for i in range(0, 10):
= sb + str(fibonacci(i)) + ", ";
sb print(sb)
## 0, 1, 1, 2, 3, 5, 8, 13, 21, 34,
Sử dụng đệ quy:
def fibonacci(n):
if (n < 0):
return -1;
elif (n == 0 or n == 1):
return n;
else:
return fibonacci(n - 1) + fibonacci(n - 2);
print("10 số đầu tiên của dãy số fibonacci: ");
## 10 số đầu tiên của dãy số fibonacci:
= "";
sb for i in range(0, 10):
= sb + str(fibonacci(i)) + ", ";
sb print(sb)
## 0, 1, 1, 2, 3, 5, 8, 13, 21, 34,
Bài tập 9: Viết chương trình tìm USCLN của 2 số a và b nhập vào từ bàn phím theo 2 phương pháp:
Viết hàm tìm UCLN theo phương pháp bù trừ
Viết hàm tìm UCLN theo giải thuật Euclid
Vào thế kỷ 3 TCN, nhà toán học Euclid (phiên âm tiếng Việt là Ơ-clit) đã phát minh ra một giải thuật tìm USCLN của hai số nguyên dương rất hiệu quả được gọi là giải thuật Euclid(. Cụ thể về ý tưởng của bài toán, giả sử a lớn hơn b, khi đó việc tính UCSLN của a và b sẽ được đưa về bài toán tính USCLN của a mod b và b vì USCLN(a, b) = USCLN(a mod b, b).
Euclid Algorithm
Euclid Algorithm2
def uscln(a, b):
if (b == 0):
return a;
return uscln(b, a % b);
def bscnn(a, b):
return int((a * b) / uscln(a, b));
= 10
a = 16
b #tính USCLN của a và b
print("Ước số chung lớn nhất của", a, "và", b, "là:", uscln(a, b));
#tính BSCNN của a và b
## Ước số chung lớn nhất của 10 và 16 là: 2
print("Bội số chung nhỏ nhất của", a, "và", b, "là:", bscnn(a, b));
## Bội số chung nhỏ nhất của 10 và 16 là: 80
Nội dung hàm GCD bằng phương pháp bù trừ:
def uscln(a, b):
= a;
temp1 = b;
temp2 while (temp1 != temp2):
if (temp1 > temp2):
-= temp2;
temp1 else:
-= temp1;
temp2 = temp1;
uscln return uscln;
def bscnn(a, b):
return int((a * b) / uscln(a, b));
= 10
a = 16
b #tính USCLN của a và b
print("Ước số chung lớn nhất của", a, "và", b, "là:", uscln(a, b));
#tính BSCNN của a và b
## Ước số chung lớn nhất của 10 và 16 là: 2
print("Bội số chung nhỏ nhất của", a, "và", b, "là:", bscnn(a, b));
## Bội số chung nhỏ nhất của 10 và 16 là: 80