Hướng dẫn python nested function performance - hiệu suất chức năng lồng nhau của python

Trong Python 3.9, các chức năng lồng nhau chậm hơn đáng ngạc nhiên so với các hàm bình thường, khoảng 10% cho ví dụ của tôi.

from timeit import timeit

def f[]:
    return 0

def factory[]:
    def g[]:
        return 0

    return g

g = factory[]

print[timeit["f[]", globals=globals[]]]
#> 0.074835498
print[timeit["g[]", globals=globals[]]]
#> 0.08470309999999998

import inspect
g.__code__ = g.__code__.replace[co_flags=g.__code__.co_flags ^ inspect.CO_NESTED]
print[timeit["f[]", globals=globals[]]]
#> 0.07321161100000001
print[timeit["g[]", globals=globals[]]]
#> 0.07439838800000001
0 hiển thị cùng một mã byte và sự khác biệt duy nhất tôi tìm thấy là trong các cờ nội bộ chức năng. Thật vậy,
import inspect
g.__code__ = g.__code__.replace[co_flags=g.__code__.co_flags ^ inspect.CO_NESTED]
print[timeit["f[]", globals=globals[]]]
#> 0.07321161100000001
print[timeit["g[]", globals=globals[]]]
#> 0.07439838800000001
1 cho thấy
import inspect
g.__code__ = g.__code__.replace[co_flags=g.__code__.co_flags ^ inspect.CO_NESTED]
print[timeit["f[]", globals=globals[]]]
#> 0.07321161100000001
print[timeit["g[]", globals=globals[]]]
#> 0.07439838800000001
2 có cờ
import inspect
g.__code__ = g.__code__.replace[co_flags=g.__code__.co_flags ^ inspect.CO_NESTED]
print[timeit["f[]", globals=globals[]]]
#> 0.07321161100000001
print[timeit["g[]", globals=globals[]]]
#> 0.07439838800000001
3 trong khi
import inspect
g.__code__ = g.__code__.replace[co_flags=g.__code__.co_flags ^ inspect.CO_NESTED]
print[timeit["f[]", globals=globals[]]]
#> 0.07321161100000001
print[timeit["g[]", globals=globals[]]]
#> 0.07439838800000001
4 thì không.

Tuy nhiên, các cờ có thể được gỡ bỏ và nó làm cho

import inspect
g.__code__ = g.__code__.replace[co_flags=g.__code__.co_flags ^ inspect.CO_NESTED]
print[timeit["f[]", globals=globals[]]]
#> 0.07321161100000001
print[timeit["g[]", globals=globals[]]]
#> 0.07439838800000001
2 nhanh như
import inspect
g.__code__ = g.__code__.replace[co_flags=g.__code__.co_flags ^ inspect.CO_NESTED]
print[timeit["f[]", globals=globals[]]]
#> 0.07321161100000001
print[timeit["g[]", globals=globals[]]]
#> 0.07439838800000001
4.

import inspect
g.__code__ = g.__code__.replace[co_flags=g.__code__.co_flags ^ inspect.CO_NESTED]
print[timeit["f[]", globals=globals[]]]
#> 0.07321161100000001
print[timeit["g[]", globals=globals[]]]
#> 0.07439838800000001

Tôi đã cố gắng nhìn vào mã Cpython để hiểu cách

import inspect
g.__code__ = g.__code__.replace[co_flags=g.__code__.co_flags ^ inspect.CO_NESTED]
print[timeit["f[]", globals=globals[]]]
#> 0.07321161100000001
print[timeit["g[]", globals=globals[]]]
#> 0.07439838800000001
7 cờ có thể tác động đến việc thực thi chức năng, nhưng tôi không tìm thấy gì. Có bất kỳ lời giải thích nào cho sự khác biệt hiệu suất này liên quan đến cờ
import inspect
g.__code__ = g.__code__.replace[co_flags=g.__code__.co_flags ^ inspect.CO_NESTED]
print[timeit["f[]", globals=globals[]]]
#> 0.07321161100000001
print[timeit["g[]", globals=globals[]]]
#> 0.07439838800000001
7 không?

Chỉnh sửa: Loại bỏ cờ

import inspect
g.__code__ = g.__code__.replace[co_flags=g.__code__.co_flags ^ inspect.CO_NESTED]
print[timeit["f[]", globals=globals[]]]
#> 0.07321161100000001
print[timeit["g[]", globals=globals[]]]
#> 0.07439838800000001
7 dường như cũng không có tác động đến thực thi chức năng, ngoại trừ chi phí, ngay cả khi nó đã bắt được biến.: Removing
import inspect
g.__code__ = g.__code__.replace[co_flags=g.__code__.co_flags ^ inspect.CO_NESTED]
print[timeit["f[]", globals=globals[]]]
#> 0.07321161100000001
print[timeit["g[]", globals=globals[]]]
#> 0.07439838800000001
7 flag seems also to have no impact on function execution, except the overhead, even when it has captured variable.

import inspect
global_var = 40
def factory[]:
    captured_var = 2
    def g[]:
        return global_var + captured_var
    return g
g = factory[]
assert g[] == 42

g.__code__ = g.__code__.replace[co_flags=g.__code__.co_flags ^ inspect.CO_NESTED]
assert g[] == 42  # function still works as expected

Trước khi cùng xem closure là gì, chúng ta trước tiên phải hiểu được nested functions [hàm lồng] và non-local variables [các biến không cục bộ] là gì.

  • 1. Hàm lồng trong Python
  • 2. Closure trong Python
  • 3. Khi nào và tại sao nên sử dụng Closure

1. Hàm lồng trong Python

2. Closure trong Python

3. Khi nào và tại sao nên sử dụng Closure

# -----------------------------------------------------------
#Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
#@author cafedevn
#Contact: 
#Fanpage: //www.facebook.com/cafedevn
#Instagram: //instagram.com/cafedevn
#Twitter: //twitter.com/CafedeVn
#Linkedin: //www.linkedin.com/in/cafe-dev-407054199/
# -----------------------------------------------------------


# Python program to illustrate 
# nested functions 
def outerFunction[text]: 
    text = text 
  
    def innerFunction[]: 
        print[text] 
  
    innerFunction[] 
  
if __name__ == '__main__': 
    outerFunction['Hey!'] 

Một hàm được định nghĩa bên trong một hàm khác thì được gọi là hàm lồng – nested function. Các hàm lồng có thể truy cập được tới các biến nằm trong hàm mà chứa nó. Trong Python, các biến không cục bộ [non-local variables] chỉ có thể được truy cập đến khi chúng và các đối tượng truy cập đến chúng nằm trong cùng một hàm. Điều này có thể được minh họa bằng ví dụ sau:

Dưới đây là đoạn chương trình Python mô tả hàm lồng

Như bạn thấy đó, hàm innerFunction[] có thể được truy cập đến một cách dễ dàng ở bên trong phần thân của hàm outerFunction[], nhưng điều này sẽ là không thể nếu thực hiện ở bên ngoài phần thân hàm outerFunction[]. Do đó, ở đây, hàm innerFunction[] được coi là một hàm lồng [ở bên trong hàm outerFunction[]], và sử dụng biến không cục bộ là biến text.

# -----------------------------------------------------------
#Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
#@author cafedevn
#Contact: 
#Fanpage: //www.facebook.com/cafedevn
#Instagram: //instagram.com/cafedevn
#Twitter: //twitter.com/CafedeVn
#Linkedin: //www.linkedin.com/in/cafe-dev-407054199/
# -----------------------------------------------------------


def func1[]:  #Outer function
  msg = 'I belong to func1'
  def func2[]: #Nested function
      print [msg]
  return func2

Về cơ bản, phương pháp ràng buộc dữ liệu với một hàm mà không cần phải thực sự truyền chúng làm tham số cho hàm thì được gọi là Closure. Closure là một đối tượng hàm [object function] có thể ghi nhớ các giá trị nằm trong cùng một hàm với nó, kể cả khi chúng không xuất hiện trong bộ nhớ.

Ví dụ mô tả việc “ràng buộc dữ liệu với một hàm mà không cần phải thực sự truyền chúng làm tham số cho hàm”:

obj = func1[]  #binding the function to an object
obj[]
I belong to func1

Trong ví dụ này, chúng ta đã trả về hàm lồng func2[] thay vì gọi đến nó. Bằng cách này chúng ta có thể trả về toàn bộ các chức năng của hàm lồng func2[] và ràng buộc nó vào một biến để sử dụng sau này.

Kết quả đoạn code ví dụ trên là:

Closure là một nested function – hàm được lồng ở bên trong một/nhiều enclosing function khác [hàm mà chứa chính cái closure – nested function ở bên trong nó] mà có quyền truy cập đến một free variable – biến tự do của một/nhiều enclosing function đã thực thi xong. Ba đặc điểm của closure trong Python là:

– Nó là một nested function – hàm được lồng ở bên trong một hàm khác

– Nó có truyền truy cập đến các free variables – biến tự do của enclosing function chứa nó.

– Nó được trả về từ enclosing function chứa nó.

Một free variable – biến tự do là một biến mà không bị ràng buộc trong phạm vi cục bộ. Để các closure có thể làm việc với các immutable variables – biến không thể thay đổi được chẳng hạn như kiểu number, kiểu string, chúng ta phải sử dụng từ khóa nonlocal

Closure trong Python giúp chúng ta tránh được việc phải sử dụng tới các giá trị toàn cục [global values] và tăng tính che giấu dữ liệu.

# -----------------------------------------------------------
#Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
#@author cafedevn
#Contact: 
#Fanpage: //www.facebook.com/cafedevn
#Instagram: //instagram.com/cafedevn
#Twitter: //twitter.com/CafedeVn
#Linkedin: //www.linkedin.com/in/cafe-dev-407054199/
# -----------------------------------------------------------


# Python program to illustrate 
# closures 
def outerFunction[text]: 
    text = text 
  
    def innerFunction[]: 
        print[text] 
  
    return innerFunction # Note we are returning function WITHOUT parenthesis 
  
if __name__ == '__main__': 
    myFunction = outerFunction['Hey!'] 
    myFunction[] 

Một closure thì không giống với hàm bình thường, nó cho phép hàm có thể truy cập đến các biến đã được bắt [captured variables] bởi closure, các biến được bắt có thể chứa hoặc là các bản sao giá trị hoặc là tham chiếu, ngay cả khi hàm đó được gọi ở bên ngoài phạm vi vùng code chứa closure.

cafedevn:
~/Documents/Python-Programs/$ python Closures.py 
Hey!

– Ví dụ 1. minh họa về closure trong Python

Kết quả in ra là:

Từ ví dụ trên ta có thể thấy rằng:

1. Closure giúp gọi đến hàm nằm ngoài phạm vi code [scope] của nó.

# -----------------------------------------------------------
#Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
#@author cafedevn
#Contact: 
#Fanpage: //www.facebook.com/cafedevn
#Instagram: //instagram.com/cafedevn
#Twitter: //twitter.com/CafedeVn
#Linkedin: //www.linkedin.com/in/cafe-dev-407054199/
# -----------------------------------------------------------


# Python program to illustrate 
# closures 
import logging 
logging.basicConfig[filename='example.log', level=logging.INFO] 
  
  
def logger[func]: 
    def log_func[*args]: 
        logging.info[ 
            'Running "{}" with arguments {}'.format[func.__name__, args]] 
        print[func[*args]] 
    # Necessary for closure to work [returning WITHOUT parenthesis] 
    return log_func               
  
def add[x, y]: 
    return x+y 
  
def sub[x, y]: 
    return x-y 
  
add_logger = logger[add] 
sub_logger = logger[sub] 
  
add_logger[3, 3] 
add_logger[4, 5] 
  
sub_logger[10, 5] 
sub_logger[20, 10] 

2. Hàm innerFunction chỉ có phạm vi code [scope] là ở bên trong thân hàm outerFunction, nhưng nhờ việc sử dụng closure, chúng ta có thể dễ dàng mở rộng phạm vi code [scope] của nó để gọi đến một hàm nằm ngoài phạm vi code của nó [chính là hàm outerFunction].

cafedevn:
~/Documents/Python-Programs/$ python MoreOnClosures.py 
6
9
5
10

3. Khi nào và tại sao nên sử dụng Closure

Một hàm được định nghĩa bên trong một hàm khác thì được gọi là hàm lồng – nested function. Các hàm lồng có thể truy cập được tới các biến nằm trong hàm mà chứa nó. Trong Python, các biến không cục bộ [non-local variables] chỉ có thể được truy cập đến khi chúng và các đối tượng truy cập đến chúng nằm trong cùng một hàm. Điều này có thể được minh họa bằng ví dụ sau:

Dưới đây là đoạn chương trình Python mô tả hàm lồng

Như bạn thấy đó, hàm innerFunction[] có thể được truy cập đến một cách dễ dàng ở bên trong phần thân của hàm outerFunction[], nhưng điều này sẽ là không thể nếu thực hiện ở bên ngoài phần thân hàm outerFunction[]. Do đó, ở đây, hàm innerFunction[] được coi là một hàm lồng [ở bên trong hàm outerFunction[]], và sử dụng biến không cục bộ là biến text.

  • w3school
  • python.org
  • Về cơ bản, phương pháp ràng buộc dữ liệu với một hàm mà không cần phải thực sự truyền chúng làm tham số cho hàm thì được gọi là Closure. Closure là một đối tượng hàm [object function] có thể ghi nhớ các giá trị nằm trong cùng một hàm với nó, kể cả khi chúng không xuất hiện trong bộ nhớ.

Ví dụ mô tả việc “ràng buộc dữ liệu với một hàm mà không cần phải thực sự truyền chúng làm tham số cho hàm”:

  • Trong ví dụ này, chúng ta đã trả về hàm lồng func2[] thay vì gọi đến nó. Bằng cách này chúng ta có thể trả về toàn bộ các chức năng của hàm lồng func2[] và ràng buộc nó vào một biến để sử dụng sau này.
  • Kết quả đoạn code ví dụ trên là:
  • Closure là một nested function – hàm được lồng ở bên trong một/nhiều enclosing function khác [hàm mà chứa chính cái closure – nested function ở bên trong nó] mà có quyền truy cập đến một free variable – biến tự do của một/nhiều enclosing function đã thực thi xong. Ba đặc điểm của closure trong Python là:

– Nó là một nested function – hàm được lồng ở bên trong một hàm khác

  • – Nó có truyền truy cập đến các free variables – biến tự do của enclosing function chứa nó.
  • – Nó được trả về từ enclosing function chứa nó.
  • Một free variable – biến tự do là một biến mà không bị ràng buộc trong phạm vi cục bộ. Để các closure có thể làm việc với các immutable variables – biến không thể thay đổi được chẳng hạn như kiểu number, kiểu string, chúng ta phải sử dụng từ khóa nonlocal
  • Closure trong Python giúp chúng ta tránh được việc phải sử dụng tới các giá trị toàn cục [global values] và tăng tính che giấu dữ liệu.
  • Một closure thì không giống với hàm bình thường, nó cho phép hàm có thể truy cập đến các biến đã được bắt [captured variables] bởi closure, các biến được bắt có thể chứa hoặc là các bản sao giá trị hoặc là tham chiếu, ngay cả khi hàm đó được gọi ở bên ngoài phạm vi vùng code chứa closure.
  • – Ví dụ 1. minh họa về closure trong Python
  • Kết quả in ra là:
  • Từ ví dụ trên ta có thể thấy rằng:

1. Closure giúp gọi đến hàm nằm ngoài phạm vi code [scope] của nó.

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!

Bài Viết Liên Quan

Chủ Đề