Các mẫu lập trình chức năng Python

Lập trình hàm đã là một chủ đề được quan tâm trong lĩnh vực phát triển trong nhiều năm và rất nhiều ngôn ngữ đã bao gồm các cách áp dụng một số nguyên tắc của nó, bao gồm cả Python. Nhưng lập trình hàm là gì? . Để có thể sử dụng đầy đủ mô hình này, các nguyên tắc và yếu tố nhất định cần được hỗ trợ bởi ngôn ngữ. Một số nguyên tắc chính có trong bất kỳ ngôn ngữ nào là hàm thuần túy, tính bất biến, hàm bậc cao, ứng dụng một phần, đánh giá lười biếng, khớp mẫu và kiểm tra kiểu. Tất cả điều này được hỗ trợ trong Python, nhưng bài viết này sẽ tập trung vào ba điều đầu tiên được đề cập. Việc sử dụng mô hình này thoạt nghe có vẻ hơi khó hiểu, nhưng nó có một số ưu điểm giúp mã sạch hơn, mạnh mẽ hơn và dễ bảo trì hơn

chức năng thuần túy

Một chức năng có thể được coi là một hoạt động với một số loại đầu vào, sẽ trả về một đầu ra. Các hàm thuần túy là những hàm mà cùng một đầu vào sẽ luôn trả về cùng một đầu ra. Chúng có thể dự đoán được và do đó dễ làm việc với

Tất nhiên, trong Python, việc tạo các hàm thuần túy là có thể. Xem xét ví dụ sau

def powerOfTwo[x]:
   return x**2

Hàm này thuần túy, bất kể nó được thực hiện bao nhiêu lần với cùng một đầu vào cho

y = 3
def powerOfTwo[]:
   return y**2
2, kết quả sẽ giống nhau. Nếu chức năng được thay đổi thành

y = 3
def powerOfTwo[]:
   return y**2

Hàm đã trở nên không trong sạch, vì nó đang sử dụng các giá trị bên ngoài cho chính nó. Vì vậy, thực hiện nó nhiều lần có thể đưa ra các kết quả đầu ra khác nhau khi

y = 3
def powerOfTwo[]:
   return y**2
3 thay đổi

tính bất biến

Tính bất biến là một thuộc tính của một giá trị, mà một khi được xác định thì không thể thay đổi được. Điều này có thể làm giảm một số tính linh hoạt trong cách cấu trúc chương trình, nhưng nó cho phép tránh mọi tác dụng phụ và thay đổi không mong muốn đối với giá trị được sử dụng

Trong Python, đạt được tính bất biến có vẻ khá dễ dàng, nhưng nó đòi hỏi một số chú ý đến cách khai báo được sử dụng. Python có hai nhóm kiểu dữ liệu chính. có thể thay đổi và bất biến

Các kiểu dữ liệu không thể thay đổi đề cập đến các kiểu dữ liệu mà một khi đã khai báo thì không thể thay đổi được. Điều đó không có nghĩa là một biến không thể được gán lại, nhưng việc tạo một bản sao của một khai báo hiện có dẫn đến một khai báo hoàn toàn mới [gán mới trong bộ nhớ]. Hãy xem xét những điều sau đây

Các biến

y = 3
def powerOfTwo[]:
   return y**2
4 và
y = 3
def powerOfTwo[]:
   return y**2
5 sẽ lưu trữ cùng một giá trị, nhưng chúng là các khai báo hoàn toàn khác nhau [sử dụng các khoảng trống khác nhau trong bộ nhớ]. Vì vậy, gán lại
y = 3
def powerOfTwo[]:
   return y**2
5 cho một cái gì đó như

văn bản sẽ không bị ảnh hưởng theo bất kỳ cách nào. Điều đó có vẻ hiển nhiên nhưng không phải lúc nào cũng đúng. Các kiểu dữ liệu bất biến là int, float, decimal, bool, string, tuple và range

Các kiểu dữ liệu có thể thay đổi là những kiểu dữ liệu đã được khai báo có thể được thay đổi. Danh sách là một trong những kiểu dữ liệu này. Trong trường hợp của danh sách, sự thay đổi này cho phép thêm, xóa hoặc thay thế các giá trị trong danh sách. Đồng thời, việc gán một biến mới, từ một khai báo danh sách đã tồn tại, sẽ không dẫn đến định nghĩa danh sách mới mà thay vào đó, một bản sao nông sẽ được tạo [bộ nhớ được chia sẻ]. Những thay đổi được thực hiện trong một danh sách sẽ ảnh hưởng đến danh sách khác. Lấy những thứ sau

Cả

y = 3
def powerOfTwo[]:
   return y**2
7 và
y = 3
def powerOfTwo[]:
   return y**2
8 sẽ kết thúc giống nhau. Hành vi này có thể là một trở ngại để đạt được mã hoàn toàn bất biến, vì vậy chúng phải được sử dụng cẩn thận. Các cách để tránh điều này là tạo các bản sao sâu trong quá trình gán hoặc tránh khai báo danh sách dựa trên danh sách hiện có. Các kiểu dữ liệu có thể thay đổi khác là danh sách, từ điển, bộ và lớp do người dùng định nghĩa

Nếu muốn có tính bất biến hoàn toàn, nên tránh các loại dữ liệu có thể thay đổi hoặc trong trường hợp các lớp dữ liệu sử dụng đối số cố định có thể hữu ích. Tuy nhiên, điều đó sẽ có rất nhiều hạn chế, do đó, một giải pháp thay thế là sử dụng các kỹ thuật tránh bất kỳ đột biến nào đối với các loại dữ liệu có thể thay đổi

Các hàm bậc cao hơn

Các loại hàm này là những hàm có thể lấy các hàm khác làm đối số của chúng hoặc kết quả là trả về một hàm. Với sự trợ giúp của những điều này, các chức năng có thể được kết hợp với nhau và có thể đạt được hành vi phức tạp hơn. Khi có chức năng bậc cao hơn, người ta nói rằng ngôn ngữ coi chức năng là công dân hạng nhất. Python là một trong những ngôn ngữ đó. Trong Python, việc thêm một hàm làm tham số của một hàm khác là chuyện nhỏ và hoạt động giống như bất kỳ tham số nào khác. Lấy ví dụ

def sumFive[x]:
   return x + 5

def doTwice[func, *val]:
   return func[func[*val]]

print[doTwice[sumFive,5]]

Chức năng phổ biến

Các hàm thuần túy, tính bất biến và các hàm bậc cao hơn là những thứ có thể đạt được trong Python và do đó, các nguyên tắc lập trình hàm có thể được áp dụng trong python. Nhưng, nếu đó là thứ duy nhất có sẵn, thì việc sử dụng lập trình chức năng trong Python sẽ trở nên phức tạp và sẽ yêu cầu mã hóa nhiều hơn dự kiến. Do đó, có rất nhiều hàm phổ biến hữu ích [xem funcools] sử dụng các nguyên tắc lập trình hàm cho phép làm việc với cấu trúc dữ liệu và luồng chương trình. Các chức năng được đề cập là bản đồ, bộ lọc, thu nhỏ, đây là những chức năng chính cho phép tiếp cận chức năng trong khi viết mã

bản đồ

là một chức năng nhận một chức năng khác và sau đó áp dụng nó cho từng phần tử của một đối tượng có thể lặp lại. Ngoài ra, bản đồ hỗ trợ nhiều lần lặp lại dưới dạng thuộc tính. Chức năng có thể được mô tả như

y = 3
def powerOfTwo[]:
   return y**2
9

Trong trường hợp có nhiều lần lặp, hàm được chuyển tới bản đồ sẽ chấp nhận số đối số bằng số lần lặp được truyền. Ngoài ra, bản đồ sẽ ngừng lặp lại khi sử dụng hết lần lặp ngắn nhất. Một điều cần lưu ý là sau khi bản đồ được thực thi, giá trị kết quả sẽ cần được chuyển đổi thành trình vòng lặp mong muốn. Có thể thấy, hàm này cho phép tạo một iterator mới từ một iterator hiện có. Đồng thời, nó có một số lợi thế rõ ràng so với mệnh đề for truyền thống [hoặc một lúc]. Xem xét ví dụ sau

list1 = [1,2,3,4,5,6,7]

for index, value in enumerate[list1]:
    list1[index] = value*2

Trong khối mã này, những gì đang được thực hiện là lấy một danh sách và sau đó cố gắng sao chép từng giá trị của nó. Bản thân mã có vẻ ổn, nhưng có một số điều cần xem xét. Rõ ràng là một tuyên bố hiện có một đột biến. Để duy trì cách tiếp cận lập trình chức năng, cách tiếp cận này sẽ không được chấp nhận. Ngoài ra, thao tác này đi kèm với rủi ro nếu mã thay đổi mạnh trong tương lai. Sử dụng bản đồ, mã sẽ kết thúc như thế này

list1 = [1,2,3,4,5,6,7]
def byTwo[x]:
    return x*2
list2 = list[map[byTwo,list1]]

Trong trường hợp này, các thao tác không hiển thị rõ ràng, thay vào đó, một danh sách mới được tạo bằng bản đồ. Ngoài ra, mã tạo danh sách mới ngắn hơn và súc tích hơn. Thậm chí có thể nói rằng nó dễ đọc hơn

lọc

khá giống với bản đồ, một chức năng và một lần lặp [lần này nhiều lần lặp không được hỗ trợ] được thực hiện. Tuy nhiên, thay vì áp dụng hàm cho từng phần tử của iterable để thay đổi giá trị của chúng, hàm sẽ xác định xem giá trị có được đưa vào iterable mới được tạo hay không. Nếu hàm trả về giá trị trung thực, phần tử được đánh giá sẽ ở lại; . Bộ lọc được định nghĩa như sau

def sumFive[x]:
   return x + 5

def doTwice[func, *val]:
   return func[func[*val]]

print[doTwice[sumFive,5]]
0

Cũng giống như bản đồ, sau khi bộ lọc được thực thi, vẫn cần phải chuyển đổi kết quả thành lần lặp mong muốn. Ưu điểm của việc sử dụng bộ lọc so với cấu trúc for/while truyền thống vẫn giống như trước đây. Hãy xem xét những điều sau đây

list1 = [1,2,3,4,5,6,7]
list2 = []
for val in list1:
    if val % 2 == 0:
        list2.append[val]
# list2 will equal [2,4,6]

Lần này, tất cả các phần tử chẵn đang được thêm vào một danh sách mới. Cách tiếp cận hoạt động tốt và khai báo đầu tiên không bị thao túng, nhưng vẫn tồn tại đột biến trong khai báo thứ hai [danh sách trống]. Sử dụng bộ lọc sẽ tránh được những vấn đề này và kết thúc với mã trông sạch hơn

def isEven[x]:
    return x % 2 == 0

list1 = [1,2,3,4,5,6,7]
list2 = list[filter[isEven, list1]]
# list2 will equal [2,4,6]

giảm

là một chức năng khá đặc biệt. Trước hết, nó không có sẵn dưới dạng bản đồ hoặc bộ lọc, bạn cần nhập nó từ một mô-đun có tên là funcools. Thứ hai, chức năng này khá linh hoạt, nó cho phép chuyển đổi một iterable sang bất kỳ cấu trúc mong muốn nào khác [có thể là một int, một đối tượng, một từ điển, v.v.]. Giảm được định nghĩa là

def sumFive[x]:
   return x + 5

def doTwice[func, *val]:
   return func[func[*val]]

print[doTwice[sumFive,5]]
1

Cách nó được sử dụng khá đơn giản; . Hàm cần 2 thuộc tính; . Nó có thể hơi khó hiểu, lấy ví dụ sau

from functools import reduce

def sum[prev, current]:
    return prev + current

result = reduce[sum, [1,2,3,4,5]]

kết quả trong trường hợp này sẽ có giá trị là

def sumFive[x]:
   return x + 5

def doTwice[func, *val]:
   return func[func[*val]]

print[doTwice[sumFive,5]]
2. Phân tách toàn bộ hoạt động, những điều sau đây sẽ được quan sát.
def sumFive[x]:
   return x + 5

def doTwice[func, *val]:
   return func[func[*val]]

print[doTwice[sumFive,5]]
3. Đó là một cuộc gọi đệ quy trên toàn bộ chức năng có thể lặp lại được cung cấp. Khi không có giá trị ban đầu nào được cung cấp, giá trị ban đầu sẽ là giá trị đầu tiên của lần lặp. Với một giá trị ban đầu

result = reduce[sum, [1,2,3],10] 

Kết quả sẽ tương đương với.

def sumFive[x]:
   return x + 5

def doTwice[func, *val]:
   return func[func[*val]]

print[doTwice[sumFive,5]]
4. Có thể thấy, giá trị ban đầu được lấy trong lệnh gọi hàm đầu tiên thay vì phần tử đầu tiên của iterable. Theo cách tương tự, như các hàm trước đã khám phá, ưu điểm của việc sử dụng reduce là chúng cho phép viết mã ngắn hơn và giúp duy trì một số dạng bất biến đối với dữ liệu được sử dụng. Ngoài ra, đối với một số thao tác rất đơn giản có thể được thực hiện với rút gọn, đã có sẵn một số chức năng giúp đơn giản hóa quy trình. Ví dụ: để tính tổng tất cả các giá trị trong danh sách, có thể sử dụng sum

Các chức năng khác có thể được sử dụng là prod, max, min, len, all, any, v.v. Tốt hơn là sử dụng cái này thay vì giảm nếu chúng đáp ứng nhu cầu của chúng tôi. Điều quan trọng cần lưu ý là một số chức năng này được tích hợp sẵn trong python và những chức năng khác cần được nhập từ funcools

hiểu

Như đã thấy trước đây, bản đồ và bộ lọc có thể được sử dụng làm cách tạo các lần lặp mới từ các lần lặp hiện có. Chúng là những cách khá hay để có cách tiếp cận chức năng hơn trong mã của chúng ta, nhưng chúng không phải là cách duy nhất để đạt được điều tương tự trong Python. Hiểu là một cách xây dựng các lần lặp trong python từ những cái hiện có với cấu trúc đơn giản và ngắn gọn. Có bốn loại hiểu trong python

  • hiểu danh sách
  • hiểu từ điển
  • Đặt mức độ hiểu
  • hiểu máy phát điện

Sử dụng bản đồ, cách sử dụng như sau

list1 = [1,2,3,4,5,6,7]
def byTwo[x]:
    return x*2
list2 = list[map[byTwo,list1]]

Với khả năng hiểu danh sách, điều này có thể được thay đổi thành

Như có thể thấy, nó ngắn gọn hơn nhiều [bản đồ có thể ngắn gọn như thế này khi sử dụng lambdas, còn được gọi là hàm ẩn danh] và quan trọng hơn, không cần chuyển đổi kết quả thành danh sách, điều đó được ngụ ý trong cấu trúc. Phá vỡ điều này, ba phần tồn tại

  • [ ] đại diện cho trình vòng lặp kết quả.
    def sumFive[x]:
       return x + 5
    
    def doTwice[func, *val]:
       return func[func[*val]]
    
    print[doTwice[sumFive,5]]
    
    5 dành cho danh sách,
    def sumFive[x]:
       return x + 5
    
    def doTwice[func, *val]:
       return func[func[*val]]
    
    print[doTwice[sumFive,5]]
    
    6 dành cho từ điển và bộ, và
    def sumFive[x]:
       return x + 5
    
    def doTwice[func, *val]:
       return func[func[*val]]
    
    print[doTwice[sumFive,5]]
    
    7 dành cho trình tạo
  • x*2 là hoạt động, nó có thể là bất kỳ loại hoạt động nào và rất phức tạp [càng đơn giản càng tốt]. Không có tuyên bố được cho phép, mặc dù. Trong trường hợp từ điển, một chìa khóa. giá trị cần được xác định
  • đối với x trong list1 là phép lặp được thực hiện, nó có thể có nhiều dạng và có thể lặp lại trên bất kỳ lần lặp nào, ngay cả khi việc hiểu tạo ra thứ gì đó khác với nó

Một điểm khác biệt so với bản đồ là việc hỗ trợ lặp lại nhiều lần lặp cùng lúc không đơn giản và đòi hỏi nhiều công việc hơn

Bây giờ, những gì về lọc?

y = 3
def powerOfTwo[]:
   return y**2
0

Điều đó sẽ trả về tất cả các số chẵn, giống như chức năng bộ lọc. Điều kiện được trình bày thêm một khía cạnh khác cho điều này và nếu muốn có một câu lệnh khác, cấu trúc sẽ thay đổi một chút như thế này

y = 3
def powerOfTwo[]:
   return y**2
1

Nhìn chung, khả năng hiểu là một giải pháp thay thế tốt cho ánh xạ và bộ lọc, và thậm chí còn tốt hơn vì hầu hết thời gian nó dẫn đến mã ngắn hơn và súc tích hơn. Tuy nhiên, mọi thứ có thể trở nên rất phức tạp với khả năng hiểu và nếu không cẩn thận, người ta có thể kết thúc với mã rất khó đọc. Vì vậy, một cách thực hành tốt là giữ cho chúng đơn giản và nếu cần các thao tác phức tạp, tốt nhất là có một chức năng khác được gọi bên trong phần hiểu

Một số cân nhắc

Có một cách tiếp cận chức năng đối với mã trong python có thể mang lại nhiều lợi thế cho cách thức hoạt động của mã và mức độ dễ dàng để làm việc với nó. Có các hàm thuần túy, duy trì một số cảm giác bất biến và sử dụng các hàm bậc cao hơn có thể làm cho mã sạch hơn và tránh các sự cố không mong muốn. Các chức năng và công cụ được trình bày [bản đồ, bộ lọc, thu nhỏ, hiểu] là những cách tuyệt vời để đạt được điều này. Tuy nhiên, có nhiều công cụ khác không được đề cập, rất hữu ích [trang trí, khớp mẫu, v.v.], và hơn thế nữa, những cách tiếp cận này không làm mất hiệu lực các cách phổ biến hơn để lặp hoặc xây dựng mã. Dưới đây là một số điểm cần xem xét

4 loại hàm trong Python là gì?

Sau đây là các loại Hàm Python khác nhau. .
Hàm tích hợp Python
Hàm đệ quy Python
Hàm Lambda trong Python
Các hàm do người dùng định nghĩa trong Python

Là Python OOP hay lập trình chức năng?

C++ và Python là những ngôn ngữ hỗ trợ lập trình hướng đối tượng , nhưng không bắt buộc sử dụng các tính năng hướng đối tượng. Lập trình chức năng phân tách một vấn đề thành một tập hợp các chức năng.

Python có tốt cho các mẫu thiết kế không?

Python là một ngôn ngữ động [tôi đã nói điều đó rồi phải không?] và do đó, Python đã triển khai hoặc giúp dễ dàng triển khai một số mẫu thiết kế phổ biến với một vài dòng mã. Some design patterns are built into Python, so we use them even without knowing.

Các mô-đun lập trình chức năng trong Python là gì?

Các mô-đun được mô tả trong chương này cung cấp các hàm và lớp hỗ trợ kiểu lập trình hàm và các thao tác chung trên các hàm có thể gọi được .

Chủ Đề