functools --- Các hàm và thao tác bậc cao hơn trên các đối tượng có thể gọi được

Source code: Lib/functools.py


Mô-đun functools dành cho các hàm bậc cao hơn: các hàm hoạt động hoặc trả về các hàm khác. Nói chung, bất kỳ đối tượng có thể gọi nào cũng có thể được coi là một hàm cho mục đích của mô-đun này.

Mô-đun functools xác định các chức năng sau:

@functools.cache(user_function)

Bộ đệm chức năng nhẹ không giới hạn đơn giản. Đôi khi được gọi là "memoize".

Trả về giống như lru_cache(maxsize=None), tạo một lớp bao bọc mỏng xung quanh việc tra cứu từ điển cho các đối số của hàm. Bởi vì nó không bao giờ cần loại bỏ các giá trị cũ nên giá trị này nhỏ hơn và nhanh hơn lru_cache() với giới hạn kích thước.

Ví dụ:

@cache
giai thừa def (n):
    trả về n * giai thừa(n-1) nếu n khác 1

>>> giai thừa(10) # no kết quả được lưu trong bộ nhớ đệm trước đó, thực hiện 11 lệnh gọi đệ quy
3628800
>>> giai thừa(5) # no cuộc gọi mới, chỉ trả về kết quả được lưu trong bộ nhớ đệm
120
>>> giai thừa(12) # two lệnh gọi đệ quy mới, giai thừa(10) được lưu vào bộ nhớ đệm
479001600

Bộ đệm là luồng an toàn để có thể sử dụng chức năng được bao bọc trong nhiều luồng. Điều này có nghĩa là cấu trúc dữ liệu cơ bản sẽ vẫn mạch lạc trong quá trình cập nhật đồng thời.

Hàm được bao bọc có thể được gọi nhiều lần nếu một luồng khác thực hiện cuộc gọi bổ sung trước khi cuộc gọi ban đầu được hoàn thành và được lưu vào bộ đệm.

Added in version 3.9.

@functools.cached_property(func)

Chuyển đổi một phương thức của một lớp thành một thuộc tính có giá trị được tính toán một lần và sau đó được lưu vào bộ đệm như một thuộc tính bình thường trong vòng đời của phiên bản. Tương tự như property(), có thêm bộ nhớ đệm. Hữu ích cho các thuộc tính tính toán đắt tiền của các phiên bản mà về mặt thực tế là không thể thay đổi được.

Ví dụ:

Tập dữ liệu lớp:

    def __init__(self, dãy_of_numbers):
        self._data = tuple(sequence_of_numbers)

    @cached_property
    def stdev(tự):
        trả về số liệu thống .stdev(self._data)

Cơ chế của cached_property() hơi khác so với property(). Thuộc tính khối thuộc tính thông thường ghi trừ khi một setter được xác định. Ngược lại, cached_property cho phép ghi.

Trình trang trí cached_property chỉ chạy khi tra cứu và chỉ khi thuộc tính cùng tên không tồn tại. Khi nó chạy, cached_property ghi vào thuộc tính có cùng tên. Việc đọc và ghi thuộc tính tiếp theo được ưu tiên hơn phương thức cached_property và nó hoạt động giống như một thuộc tính bình thường.

Giá trị được lưu trong bộ nhớ cache có thể được xóa bằng cách xóa thuộc tính. Điều này cho phép phương thức cached_property chạy lại.

Zz000zz không ngăn chặn tình trạng chạy đua có thể xảy ra khi sử dụng đa luồng. Hàm getter có thể chạy nhiều lần trên cùng một phiên bản, với lần chạy mới nhất sẽ đặt giá trị được lưu trong bộ nhớ đệm. Nếu thuộc tính được lưu trong bộ nhớ đệm là bình thường hoặc không có hại khi chạy nhiều lần trên một phiên bản thì điều này không sao. Nếu cần đồng bộ hóa, hãy triển khai khóa cần thiết bên trong hàm getter được trang trí hoặc xung quanh quyền truy cập thuộc tính được lưu trong bộ nhớ đệm.

Lưu ý, trình trang trí này cản trở hoạt động của từ điển chia sẻ khóa PEP 412. Điều này có nghĩa là các từ điển phiên bản có thể chiếm nhiều dung lượng hơn bình thường.

Ngoài ra, trình trang trí này yêu cầu thuộc tính __dict__ trên mỗi phiên bản phải là ánh xạ có thể thay đổi. Điều này có nghĩa là nó sẽ không hoạt động với một số loại, chẳng hạn như siêu dữ liệu (vì thuộc tính __dict__ trên các thể hiện loại là proxy chỉ đọc cho không gian tên lớp) và những thuộc tính chỉ định __slots__ mà không bao gồm __dict__ là một trong các vị trí được xác định (vì các lớp như vậy hoàn toàn không cung cấp thuộc tính __dict__).

Nếu không có ánh xạ có thể thay đổi hoặc nếu muốn chia sẻ khóa tiết kiệm không gian thì cũng có thể đạt được hiệu ứng tương tự như cached_property() bằng cách xếp chồng property() lên trên lru_cache(). Xem Làm cách nào để lưu vào bộ nhớ đệm các cuộc gọi phương thức? để biết thêm chi tiết về sự khác biệt của điều này với cached_property().

Added in version 3.8.

Thay đổi trong phiên bản 3.12: Trước Python 3.12, cached_property đã bao gồm một khóa không có giấy tờ để đảm bảo rằng khi sử dụng đa luồng, hàm getter được đảm bảo chỉ chạy một lần cho mỗi phiên bản. Tuy nhiên, khóa là theo thuộc tính chứ không phải theo từng phiên bản, điều này có thể dẫn đến tình trạng xung đột khóa cao không thể chấp nhận được. Trong Python 3.12+ khóa này bị loại bỏ.

functools.cmp_to_key(func)

Chuyển đổi hàm so sánh kiểu cũ thành key function. Được sử dụng với các công cụ chấp nhận các chức năng chính (như sorted(), min(), max(), heapq.nlargest(), heapq.nsmallest(), itertools.groupby()). Hàm này chủ yếu được sử dụng làm công cụ chuyển tiếp cho các chương trình được chuyển đổi từ Python 2 hỗ trợ sử dụng các hàm so sánh.

Hàm so sánh là bất kỳ hàm có thể gọi nào chấp nhận hai đối số, so sánh chúng và trả về số âm cho giá trị nhỏ hơn, 0 cho đẳng thức hoặc số dương cho giá trị lớn hơn. Hàm khóa là một hàm có thể gọi được, chấp nhận một đối số và trả về một giá trị khác được sử dụng làm khóa sắp xếp.

Ví dụ:

được sắp xếp(iterable, key=cmp_to_key(locale.strcoll)) thứ tự sắp xếp # locale-aware

Để biết các ví dụ về sắp xếp và hướng dẫn sắp xếp ngắn gọn, hãy xem Kỹ thuật sắp xếp.

Added in version 3.2.

@functools.lru_cache(user_function)
@functools.lru_cache(maxsize=128, typed=False)

Công cụ trang trí để bao bọc một chức năng bằng một lệnh gọi có thể ghi nhớ giúp tiết kiệm tới maxsize các cuộc gọi gần đây nhất. Nó có thể tiết kiệm thời gian khi một hàm đắt tiền hoặc hàm ràng buộc I/O được gọi định kỳ với cùng các đối số.

Bộ đệm là luồng an toàn để có thể sử dụng chức năng được bao bọc trong nhiều luồng. Điều này có nghĩa là cấu trúc dữ liệu cơ bản sẽ vẫn mạch lạc trong quá trình cập nhật đồng thời.

Hàm được bao bọc có thể được gọi nhiều lần nếu một luồng khác thực hiện cuộc gọi bổ sung trước khi cuộc gọi ban đầu được hoàn thành và được lưu vào bộ đệm.

Vì từ điển được sử dụng để lưu vào bộ nhớ đệm kết quả nên các đối số từ khóa và vị trí của hàm phải là hashable.

Các mẫu đối số riêng biệt có thể được coi là các lệnh gọi riêng biệt với các mục nhập bộ đệm riêng biệt. Ví dụ: f(a=1, b=2)f(b=2, a=1) khác nhau về thứ tự đối số từ khóa và có thể có hai mục nhập bộ đệm riêng biệt.

Nếu user_function được chỉ định thì nó phải có thể gọi được. Điều này cho phép trang trí lru_cache được áp dụng trực tiếp cho hàm người dùng, để maxsize ở giá trị mặc định là 128:

@lru_cache
def count_nguyên âm(câu):
    trả về tổng(câu.count(nguyên âm) cho nguyên âm trong 'AEIOUaeiou')

Nếu maxsize được đặt thành None, tính năng LRU sẽ bị tắt và bộ đệm có thể phát triển không giới hạn.

Nếu typed được đặt thành true, các đối số hàm thuộc các loại khác nhau sẽ được lưu vào bộ đệm riêng. Nếu typed sai, quá trình triển khai thường sẽ coi chúng là các lệnh gọi tương đương và chỉ lưu vào bộ đệm một kết quả duy nhất. (Một số loại như strint có thể được lưu vào bộ nhớ đệm riêng ngay cả khi typed sai.)

Lưu ý, tính đặc hiệu của kiểu chỉ áp dụng cho các đối số trực tiếp của hàm chứ không áp dụng cho nội dung của chúng. Các đối số vô hướng, Decimal(42)Fraction(42) được coi là các lệnh gọi riêng biệt với kết quả riêng biệt. Ngược lại, các đối số bộ ('answer', Decimal(42))('answer', Fraction(42)) được coi là tương đương.

Hàm được bao bọc được trang bị hàm cache_parameters() trả về một dict mới hiển thị các giá trị cho maxsizetyped. Đây chỉ là mục đích thông tin. Thay đổi các giá trị không có hiệu lực.

Để giúp đo lường hiệu quả của bộ đệm và điều chỉnh tham số maxsize, hàm được bao bọc được trang bị một hàm cache_info() trả về named tuple hiển thị hits, misses, maxsizecurrsize.

Trình trang trí cũng cung cấp chức năng cache_clear() để xóa hoặc vô hiệu hóa bộ đệm.

Hàm cơ bản ban đầu có thể được truy cập thông qua thuộc tính __wrapped__. Điều này rất hữu ích cho việc xem xét nội tâm, bỏ qua bộ đệm hoặc để gói lại chức năng bằng một bộ đệm khác.

Bộ đệm giữ các tham chiếu đến các đối số và trả về các giá trị cho đến khi chúng hết bộ đệm hoặc cho đến khi bộ đệm bị xóa.

Nếu một phương thức được lưu vào bộ đệm, đối số phiên bản self sẽ được đưa vào bộ đệm. Xem Làm cách nào để lưu vào bộ nhớ đệm các cuộc gọi phương thức?

LRU (least recently used) cache hoạt động tốt nhất khi các cuộc gọi gần đây nhất là dự báo tốt nhất về các cuộc gọi sắp tới (ví dụ: các bài viết phổ biến nhất trên máy chủ tin tức có xu hướng thay đổi mỗi ngày). Giới hạn kích thước của bộ đệm đảm bảo rằng bộ đệm không phát triển mà không bị ràng buộc bởi các quy trình chạy dài như máy chủ web.

Nói chung, bộ đệm LRU chỉ nên được sử dụng khi bạn muốn sử dụng lại các giá trị đã tính toán trước đó. Theo đó, sẽ không có ý nghĩa gì nếu lưu vào bộ đệm các hàm có tác dụng phụ, các hàm cần tạo các đối tượng có thể thay đổi riêng biệt trên mỗi lệnh gọi (chẳng hạn như hàm tạo và hàm không đồng bộ) hoặc các hàm không tinh khiết như time() hoặc Random().

Ví dụ về bộ đệm LRU cho nội dung web tĩnh:

@lru_cache(maxsize=32)
chắc chắn get_pep(num):
    'Truy xuất văn bản của Đề xuất cải tiến Python'
    tài nguyên = f'https://peps.python.org/pep-{num:04d}'
    thử:
        với urllib.request.urlopen(resource)  s:
            trả về s.read()
    ngoại trừ urllib.error.HTTPError:
        trả về 'Không tìm thấy'

>>> cho n trong 8, 290, 308, 320, 8, 218, 320, 279, 289, 320, 9991:
... pep = get_pep(n)
... print(n, len(pep))

>>> get_pep.cache_info()
CacheInfo(lần truy cập=3, bỏ lỡ=8, maxsize=32, Currsize=8)

Ví dụ về tính toán hiệu quả Fibonacci numbers bằng cách sử dụng bộ đệm để triển khai kỹ thuật dynamic programming

@lru_cache(maxsize=Không)
def fib(n):
    nếu n < 2:
        trả lại n
    trả về fib(n-1) + fib(n-2)

>>> [fib(n) cho n trong phạm vi (16)]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]

>>> fib.cache_info()
CacheInfo(lần truy cập=28, bỏ lỡ=16, maxsize=Không , currsize=16)

Added in version 3.2.

Thay đổi trong phiên bản 3.3: Đã thêm tùy chọn typed.

Thay đổi trong phiên bản 3.8: Đã thêm tùy chọn user_function.

Thay đổi trong phiên bản 3.9: Đã thêm chức năng cache_parameters()

@functools.total_ordering

Với một lớp xác định một hoặc nhiều phương thức sắp xếp so sánh phong phú, trình trang trí lớp này sẽ cung cấp phần còn lại. Điều này giúp đơn giản hóa nỗ lực liên quan đến việc chỉ định tất cả các hoạt động so sánh phong phú có thể có:

Lớp phải xác định một trong các __lt__(), __le__(), __gt__() hoặc __ge__(). Ngoài ra, lớp nên cung cấp phương thức __eq__().

Ví dụ:

@total_ordering
học sinh lớp:
    def _is_valid_operand(tự, khác):
        return (hasattr(other, "lastname") 
                hasattr(khác, "tên"))
    def __eq__(bản thân, người khác):
        nếu không self._is_valid_operand(other):
            trả về Chưa thực hiện
        trả về ((self.lastname.low(), self.firstname.low()) ==
                (other.lastname.low(), other.firstname.low()))
    def __lt__(bản thân, người khác):
        nếu không self._is_valid_operand(other):
            trả về Chưa thực hiện
        trả về ((self.lastname.low(), self.firstname.low()) <
                (other.lastname.low(), other.firstname.low()))

Ghi chú

Mặc dù trình trang trí này giúp dễ dàng tạo các kiểu được sắp xếp hoàn toàn hoạt động tốt, nhưng does lại phải trả giá bằng việc thực thi chậm hơn và dấu vết ngăn xếp phức tạp hơn đối với các phương pháp so sánh dẫn xuất. Nếu điểm chuẩn hiệu suất cho thấy đây là điểm nghẽn đối với một ứng dụng nhất định thì việc triển khai tất cả sáu phương pháp so sánh phong phú thay vào đó có thể giúp tăng tốc độ dễ dàng.

Ghi chú

Trình trang trí này không cố gắng ghi đè các phương thức đã được khai báo trong lớp or its superclasses. Có nghĩa là nếu một siêu lớp định nghĩa một toán tử so sánh, total_ordering sẽ không triển khai lại nó, ngay cả khi phương thức ban đầu là trừu tượng.

Added in version 3.2.

Thay đổi trong phiên bản 3.4: Hiện đã hỗ trợ trả về NotImplemented từ hàm so sánh cơ bản cho các loại không được nhận dạng.

functools.Placeholder

Một đối tượng đơn lẻ được sử dụng làm trọng điểm để dành chỗ cho các đối số vị trí khi gọi partial()partialmethod().

Added in version 3.14.

functools.partial(func, /, *args, **keywords)

Trả về một partial object mới mà khi được gọi sẽ hoạt động giống như func được gọi với các đối số vị trí args và các đối số từ khóa keywords. Nếu có thêm đối số được cung cấp cho cuộc gọi, chúng sẽ được thêm vào args. Nếu các đối số từ khóa bổ sung được cung cấp, chúng sẽ mở rộng và ghi đè keywords. Gần tương đương với:

def một phần(func, /, *args, **keywords):
    def newfunc(*more_args, **more_keywords):
        return func(*args, *more_args, **(keywords | more_keywords))
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = từ khóa
    trở lại newfunc

Hàm partial() được sử dụng cho ứng dụng hàm một phần nhằm "đóng băng" một số phần đối số và/hoặc từ khóa của hàm dẫn đến một đối tượng mới có chữ ký đơn giản. Ví dụ: partial() có thể được sử dụng để tạo một lệnh gọi hoạt động giống như hàm int() trong đó đối số base mặc định là 2:

>>> basetwo = một phần(int, base=2)
>>> basetwo.__doc__ = 'Chuyển chuỗi cơ số 2 thành int.'
>>> basetwo('10010')
18

Nếu Placeholder canh gác có trong args, chúng sẽ được điền trước khi partial() được gọi. Điều này giúp bạn có thể điền trước bất kỳ đối số vị trí nào bằng lệnh gọi tới partial(); không có Placeholder, chỉ có thể điền trước số lượng đối số vị trí hàng đầu đã chọn.

Nếu có bất kỳ điểm canh gác Placeholder nào, tất cả phải được điền vào thời điểm gọi:

>>> say_to_world = một phần(in, Giữ chỗ, Giữ chỗ, "thế giới!")
>>> say_to_world('Xin chào', 'thân yêu')
Xin chào thế giới thân yêu!

Việc gọi say_to_world('Hello') sẽ tạo ra TypeError, vì chỉ có một đối số vị trí được cung cấp nhưng có hai phần giữ chỗ phải được điền vào.

Nếu partial() được áp dụng cho một đối tượng partial() hiện có, các trọng điểm Placeholder của đối tượng đầu vào sẽ được điền vào các đối số vị trí mới. Một trình giữ chỗ có thể được giữ lại bằng cách chèn một trọng điểm Placeholder mới vào vị trí được giữ bởi Placeholder trước đó:

>>> từ functools nhập một phần, Giữ chỗ dưới dạng _
>>> xóa = một phần(str.replace, _, _, '')
>>> message = 'Xin chào thế giới thân yêu!'
>>> xóa(tin nhắn, 'thân yêu')
'Xin chào thế giới!'
>>> Remove_dear = một phần(xóa, _, 'thân yêu')
>>> Remove_dear(tin nhắn)
'Xin chào thế giới!'
>>> Remove_first_dear = một phần(remove_dear, _, 1)
>>> Remove_first_dear(tin nhắn)
'Xin chào thế giới thân yêu!'

Placeholder không thể được chuyển tới partial() làm đối số từ khóa.

Thay đổi trong phiên bản 3.14: Đã thêm hỗ trợ cho Placeholder trong các đối số vị trí.

class functools.partialmethod(func, /, *args, **keywords)

Trả về một bộ mô tả partialmethod mới hoạt động giống như partial ngoại trừ việc nó được thiết kế để sử dụng làm định nghĩa phương thức thay vì có thể gọi trực tiếp.

func phải là descriptor hoặc có thể gọi được (cả hai đối tượng, giống như các hàm thông thường, được xử lý dưới dạng mô tả).

Khi func là một bộ mô tả (chẳng hạn như hàm Python bình thường, classmethod(), staticmethod(), abstractmethod() hoặc một phiên bản khác của partialmethod), các lệnh gọi tới __get__ sẽ được ủy quyền cho bộ mô tả cơ bản và kết quả là một partial object thích hợp được trả về.

Khi func là một bộ mô tả không thể gọi được, một phương thức liên kết thích hợp sẽ được tạo động. Hàm này hoạt động giống như một hàm Python bình thường khi được sử dụng làm phương thức: đối số self sẽ được chèn làm đối số vị trí đầu tiên, ngay cả trước khi argskeywords được cung cấp cho hàm tạo partialmethod.

Ví dụ:

>>> lớp Ô:
... def __init__(self):
... self._alive = Sai
... @property
... def còn sống(tự):
... trở về self._alive
... def set_state(self, state):
... self._alive = bool(state)
... set_alive = một phần phương pháp (set_state, True)
... set_dead = một phần phương thức (set_state, Sai)
...
>>> c = Ô()
>>> c.alive
sai
>>> c.set_alive()
>>> c.alive
đúng

Added in version 3.4.

functools.reduce(function, iterable, /[, initial])

Áp dụng function của hai đối số tích lũy cho các mục của iterable, từ trái sang phải, để giảm khả năng lặp lại thành một giá trị duy nhất. Ví dụ: reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) tính ((((1+2)+3)+4)+5). Đối số bên trái, x, là giá trị tích lũy và đối số bên phải, y, là giá trị cập nhật từ iterable. Nếu có initial tùy chọn, nó sẽ được đặt trước các mục của iterable trong phép tính và đóng vai trò mặc định khi iterable trống. Nếu initial không được cung cấp và iterable chỉ chứa một mục thì mục đầu tiên sẽ được trả về.

Gần tương đương với:

init_missing = đối tượng()

def less(hàm, iterable, /, initial=initial_missing):
    it = iter( thể lặp lại)
    nếu ban đầu  init_missing:
        giá trị = tiếp theo()
    khác:
        giá trị = ban đầu
    cho phần tử trong đó:
        giá trị = hàm(giá trị, phần tử)
    giá trị trả về

Xem itertools.accumulate() để biết trình vòng lặp mang lại tất cả các giá trị trung gian.

Thay đổi trong phiên bản 3.14: initial hiện được hỗ trợ làm đối số từ khóa.

@functools.singledispatch

Chuyển đổi một hàm thành single-dispatch generic function.

Để xác định một hàm chung, hãy trang trí nó bằng trình trang trí @singledispatch. Khi xác định hàm bằng @singledispatch, hãy lưu ý rằng việc gửi đi xảy ra theo loại đối số đầu tiên:

>>> từ functools nhập singledispatch
>>> @singledispatch
... def fun(arg, tiết=False):
... nếu dài dòng:
... print("Để tôi nói,", end=" ")
... in(arg)

Để thêm các cách triển khai quá tải vào hàm, hãy sử dụng thuộc tính register() của hàm chung, có thể được sử dụng làm công cụ trang trí. Đối với các hàm được chú thích bằng các kiểu, trình trang trí sẽ tự động suy ra kiểu của đối số đầu tiên

>>> @fun.register
... def _(arg: int, chi tiết=False):
... nếu dài dòng:
... print("Sức mạnh về số lượng, hả?", end=" ")
... in(arg)
...
>>> @fun.register
... def _(arg: list, chi tiết=False):
... nếu dài dòng:
... print("Liệt kê cái này:")
... đối với i, elem trong enumerate(arg):
... print(i, elem)

typing.Union cũng có thể được sử dụng

>>> @fun.register
... def _(arg: int | float, chi tiết=False):
... nếu dài dòng:
... print("Sức mạnh về số lượng, hả?", end=" ")
... in(arg)
...
>>> từ cách  import Union
>>> @fun.register
... def _(arg: Union[danh sách, bộ], chi tiết=False):
... nếu dài dòng:
... print("Liệt kê cái này:")
... đối với i, elem trong enumerate(arg):
... print(i, elem)
...

Đối với mã không sử dụng chú thích kiểu, đối số kiểu thích hợp có thể được chuyển rõ ràng tới chính trình trang trí

>>> @fun.register(phức tạp)
... def _(arg, tiết=False):
... nếu dài dòng:
... print("Tốt hơn là phức tạp.", end=" ")
... print(arg.real, arg.imag)
...

Đối với mã gửi đi theo loại bộ sưu tập (ví dụ: list), nhưng muốn gõ gợi ý các mục của bộ sưu tập (ví dụ: list[int]), loại công văn phải được chuyển rõ ràng đến chính trình trang trí với gợi ý kiểu chữ đi vào định nghĩa hàm:

>>> @fun.register(danh sách)
... def _(arg: list[int], chi tiết=False):
... nếu dài dòng:
... print("Liệt kê cái này:")
... đối với i, elem trong enumerate(arg):
... print(i, elem)

Ghi chú

Trong thời gian chạy, hàm sẽ gửi đi một phiên bản của danh sách bất kể loại có trong danh sách, tức là [1,2,3] sẽ được gửi giống như ["foo", "bar", "baz"]. Chú thích được cung cấp trong ví dụ này chỉ dành cho trình kiểm tra loại tĩnh và không có tác động trong thời gian chạy.

Để cho phép đăng ký lambdas và các hàm có sẵn, thuộc tính register() cũng có thể được sử dụng ở dạng hàm:

>>> không   (arg, chi tiết=False):
... print("Không có gì.")
...
>>> fun.register(loại(Không ), không  )

Thuộc tính register() trả về hàm chưa được trang trí. Điều này cho phép xếp chồng trang trí, pickling và tạo các bài kiểm tra đơn vị cho từng biến thể một cách độc lập:

>>> @fun.register(float)
... @fun.register(Thập phân)
... def fun_num(arg, tiết=False):
... nếu dài dòng:
... print("Một nửa số của bạn:", end=" ")
... in(arg / 2)
...
>>> fun_num thật thú vị
sai

Khi được gọi, hàm chung sẽ gửi đi theo kiểu đối số đầu tiên

>>> fun("Xin chào thế giới.")
Xin chào thế giới.
>>> vui ("kiểm tra.", dài dòng=True)
Hãy để tôi nói, kiểm tra.
>>> vui vẻ(42, dài dòng=True)
Sức mạnh về số lượng phải không? 42
>>> fun(['spam', 'spam', 'trứng', 'spam'], chi tiết=True)
Liệt kê điều này:
0 thư rác
1 thư rác
2 quả trứng
3 thư rác
>>> vui vẻ(Không )
Không có gì.
>>> vui vẻ(1.23)
0,615

Khi không có triển khai được đăng ký cho một loại cụ thể, thứ tự phân giải phương thức của nó sẽ được sử dụng để tìm cách triển khai chung hơn. Hàm ban đầu được trang trí bằng @singledispatch được đăng ký cho loại object cơ sở, có nghĩa là nó được sử dụng nếu không tìm thấy cách triển khai nào tốt hơn.

Nếu một triển khai được đăng ký vào abstract base class, các lớp con ảo của lớp cơ sở sẽ được gửi đến triển khai đó:

>>> từ bản đồ nhập bộ sưu tập.abc
>>> @fun.register
... def _(arg: Ánh xạ, chi tiết=False):
... nếu dài dòng:
... print("Khóa & Giá trị")
... cho khóa, giá trị trong arg.items():
... print(key, "=>", value)
...
>>> vui vẻ({"a": "b"})
a => b

Để kiểm tra cách triển khai nào mà hàm chung sẽ chọn cho một loại nhất định, hãy sử dụng thuộc tính dispatch()

>>> fun.dispatch(float)
<hàm fun_num ở 0x1035a2840>
>>> fun.dispatch(dict) # note: triển khai mặc định
<chức năng vui vẻ ở 0x103fe0000>

Để truy cập tất cả các triển khai đã đăng ký, hãy sử dụng thuộc tính registry chỉ đọc

>>> fun.registry.keys()
dict_keys([<class 'NoneType'>, <class 'int'>, <class 'object'>,
          <lớp 'thập phân.Decimal'>, <lớp 'danh sách'>,
          <lớp 'thả nổi'>])
>>> fun.registry[float]
<hàm fun_num ở 0x1035a2840>
>>> fun.registry[object]
<chức năng vui vẻ ở 0x103fe0000>

Added in version 3.4.

Thay đổi trong phiên bản 3.7: Thuộc tính register() hiện hỗ trợ sử dụng chú thích loại.

Thay đổi trong phiên bản 3.11: Thuộc tính register() hiện hỗ trợ typing.Union dưới dạng chú thích loại.

class functools.singledispatchmethod(func)

Chuyển đổi một phương thức thành single-dispatch generic function.

Để xác định một phương thức chung, hãy trang trí nó bằng trang trí @singledispatchmethod. Khi xác định một phương thức bằng @singledispatchmethod, hãy lưu ý rằng việc gửi đi xảy ra theo kiểu đối số không phải self hoặc không phải cls đầu tiên:

Lớp phủ định:
    @singledispatchmethod
    def neg(self, arg):
        raise NotImplementedError("Không thể phủ nhận a")

    @neg.register
    def _(self, arg: int):
        trả về -arg

    @neg.register
    def _(self, arg: bool):
        trở lại không tranh luận

@singledispatchmethod hỗ trợ lồng với các trang trí khác như @classmethod. Lưu ý rằng để cho phép dispatcher.register, singledispatchmethod phải là trang trí outer most. Đây là lớp Negator với các phương thức neg được liên kết với lớp, chứ không phải là một phiên bản của lớp:

Lớp phủ định:
    @singledispatchmethod
    @classmethod
    def neg(cls, arg):
        raise NotImplementedError("Không thể phủ nhận a")

    @neg.register
    @classmethod
    def _(cls, arg: int):
        trả về -arg

    @neg.register
    @classmethod
    def _(cls, arg: bool):
        trở lại không tranh luận

Mẫu tương tự có thể được sử dụng cho các trang trí tương tự khác: @staticmethod, @~abc.abstractmethod và các mẫu khác.

Added in version 3.8.

functools.update_wrapper(wrapper, wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)

Cập nhật hàm wrapper để trông giống hàm wrapped. Các đối số tùy chọn là các bộ dữ liệu để chỉ định thuộc tính nào của hàm ban đầu được gán trực tiếp cho các thuộc tính phù hợp trên hàm bao bọc và thuộc tính nào của hàm bao bọc được cập nhật với các thuộc tính tương ứng từ hàm ban đầu. Giá trị mặc định cho các đối số này là các hằng số cấp độ mô-đun WRAPPER_ASSIGNMENTS (gán cho __module__, __name__, __qualname__, __annotations__, __type_params____doc__, chuỗi tài liệu của hàm bao bọc) và WRAPPER_UPDATES (cập nhật __dict__ của hàm bao bọc, tức là từ điển phiên bản).

Để cho phép truy cập vào hàm ban đầu nhằm mục đích xem xét nội tâm và các mục đích khác (ví dụ: bỏ qua trình trang trí bộ nhớ đệm như lru_cache()), hàm này sẽ tự động thêm thuộc tính __wrapped__ vào trình bao bọc đề cập đến hàm đang được gói.

Mục đích sử dụng chính của hàm này là trong các hàm decorator bao bọc hàm được trang trí và trả về trình bao bọc. Nếu hàm bao bọc không được cập nhật thì siêu dữ liệu của hàm được trả về sẽ phản ánh định nghĩa trình bao bọc thay vì định nghĩa hàm ban đầu, điều này thường ít hữu ích hơn.

update_wrapper() có thể được sử dụng với các lệnh gọi khác ngoài chức năng. Bất kỳ thuộc tính nào có tên trong assigned hoặc updated bị thiếu trong đối tượng được bao bọc đều bị bỏ qua (tức là hàm này sẽ không cố gắng đặt chúng trên hàm bao bọc). AttributeError vẫn được nâng lên nếu bản thân hàm bao bọc thiếu bất kỳ thuộc tính nào có tên trong updated.

Thay đổi trong phiên bản 3.2: Thuộc tính __wrapped__ hiện được thêm tự động. Thuộc tính __annotations__ hiện được sao chép theo mặc định. Các thuộc tính bị thiếu không còn kích hoạt AttributeError nữa.

Thay đổi trong phiên bản 3.4: Thuộc tính __wrapped__ hiện luôn đề cập đến hàm được bao bọc, ngay cả khi hàm đó xác định thuộc tính __wrapped__. (xem bpo-17482)

Thay đổi trong phiên bản 3.12: Thuộc tính __type_params__ hiện được sao chép theo mặc định.

@functools.wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES)

Đây là một hàm tiện lợi để gọi update_wrapper() làm công cụ trang trí hàm khi xác định hàm bao bọc. Nó tương đương với partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated). Ví dụ:

>>> từ gói nhập khẩu functools
>>> def my_designator(f):
... @wraps(f)
... trình bao bọc def(*args, **kwds):
... print('Gọi hàm trang trí')
... trả về f(*args, **kwds)
... trình bao bọc trả lại
...
>>> @my_designator
...  dụ  ràng():
... """Chuỗi tài liệu"""
... print('Gọi hàm ví dụ')
...
>>>  dụ()
Gọi hàm trang trí
Gọi là hàm ví dụ
>>>  dụ.__name__
'ví dụ'
>>>  dụ.__doc__
'Chuỗi tài liệu'

Nếu không sử dụng nhà máy trang trí này, tên của hàm ví dụ sẽ là 'wrapper' và chuỗi tài liệu của example() ban đầu sẽ bị mất.

Đối tượng partial

Đối tượng partial là đối tượng có thể gọi được được tạo bởi partial(). Chúng có ba thuộc tính chỉ đọc:

partial.func

Một đối tượng hoặc chức năng có thể gọi được. Các cuộc gọi đến đối tượng partial sẽ được chuyển tiếp đến func với các đối số và từ khóa mới.

partial.args

Các đối số vị trí ngoài cùng bên trái sẽ được thêm vào trước các đối số vị trí được cung cấp cho lệnh gọi đối tượng partial.

partial.keywords

Các đối số từ khóa sẽ được cung cấp khi đối tượng partial được gọi.

Các đối tượng partial giống như function objects ở chỗ chúng có thể gọi được, có thể tham chiếu yếu và có thể có các thuộc tính. Có một số khác biệt quan trọng. Chẳng hạn, thuộc tính __name____doc__ không được tạo tự động.