Spec-Zone.ru › Python 3.12

functools — Функции высшего порядка и операции с вызываемыми объектами

Исходный код: Lib/functools.py

Модуль functools предназначен для функций высшего порядка: функций, которые выполняют операции над другими функциями или возвращают их. В общем случае любой вызываемый объект может рассматриваться как функция для целей этого модуля.

Модуль functools определяет следующие функции:

@functools.cache(user_function)

Простой лёгкий кэш функций без ограничения размера. Иногда называется «мемоизацией».

Возвращает то же, что и lru_cache(maxsize=None), создавая тонкую обёртку вокруг поиска в словаре по аргументам функции. Поскольку старые значения никогда не требуется удалять, такая обёртка меньше и быстрее, чем lru_cache() с ограничением размера.

Например:

@cache
def factorial(n):
    return n * factorial(n-1) if n else 1

>>> factorial(10)      # no previously cached result, makes 11 recursive calls
3628800
>>> factorial(5)       # just looks up cached value result
120
>>> factorial(12)      # makes two new recursive calls, the other 10 are cached
479001600

Кэш потокобезопасен, поэтому обёрнутую функцию можно использовать в нескольких потоках. Это означает, что базовая структура данных останется целостной при параллельных обновлениях.

Обёрнутая функция может быть вызвана более одного раза, если другой поток выполнит дополнительный вызов до завершения и кэширования первоначального вызова.

Добавлено в версии 3.9.

@functools.cached_property(func)

Преобразует метод класса в свойство, значение которого вычисляется один раз, а затем кэшируется как обычный атрибут на всё время существования экземпляра. Похоже на property(), но с кэшированием. Полезно для дорогостоящих вычисляемых свойств экземпляров, которые в остальном фактически неизменяемы.

Пример:

class DataSet:

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

    @cached_property
    def stdev(self):
        return statistics.stdev(self._data)

Механизм работы cached_property() несколько отличается от property(). Обычное свойство блокирует запись атрибутов, если не определён метод-сеттер. В отличие от него, cached_property разрешает запись.

Декоратор cached_property срабатывает только при поиске атрибута и только если атрибут с таким именем ещё не существует. При срабатывании cached_property записывает значение в атрибут с тем же именем. Последующие чтения и записи атрибута имеют приоритет над методом cached_property, и он работает как обычный атрибут.

Кэшированное значение можно очистить, удалив атрибут. Это позволит методу cached_property запуститься снова.

cached_property не предотвращает возможную гонку при использовании в многопоточной среде. Функция-геттер может выполниться для одного экземпляра несколько раз, и последним кэшированным значением станет результат последнего выполнения. Если кэшируемое свойство идемпотентно или повторное выполнение для одного экземпляра не причиняет вреда, это допустимо. Если необходима синхронизация, реализуйте нужную блокировку внутри декорированной функции-геттера или вокруг доступа к кэшируемому свойству.

Обратите внимание: этот декоратор мешает работе совместно использующих ключи словарей PEP 412. В результате словари экземпляров могут занимать больше места, чем обычно.

Кроме того, этот декоратор требует, чтобы атрибут __dict__ каждого экземпляра был изменяемым отображением. Поэтому он не работает с некоторыми типами, например с метаклассами (поскольку атрибуты __dict__ экземпляров типов являются прокси только для чтения пространства имён класса), а также с типами, которые задают __slots__, не включая __dict__ в список определённых слотов (такие классы вообще не предоставляют атрибут __dict__).

Если изменяемое отображение недоступно или требуется эффективное по памяти совместное использование ключей, эффекта, подобного cached_property(), можно добиться, также используя property() поверх lru_cache(). Дополнительные сведения о различиях с cached_property() см. в разделе Как кэшировать вызовы методов?.

Добавлено в версии 3.8.

Изменено в версии 3.12: До Python 3.12 cached_property содержал недокументированную блокировку, которая гарантировала однократный запуск функции-геттера для каждого экземпляра при использовании в многопоточной среде. Однако блокировка относилась к свойству, а не к экземпляру, что могло приводить к чрезмерной конкуренции за блокировку. В Python 3.12+ эта блокировка удалена.

functools.cmp_to_key(func)

Преобразует функцию сравнения старого формата в ключевую функцию. Используется с инструментами, принимающими ключевые функции (например, sorted(), min(), max(), heapq.nlargest(), heapq.nsmallest(), itertools.groupby()). Эта функция в первую очередь служит переходным инструментом для программ, переводимых с Python 2, где поддерживалось использование функций сравнения.

Функция сравнения — это любой вызываемый объект, который принимает два аргумента, сравнивает их и возвращает отрицательное число, если первый аргумент меньше второго, ноль — если они равны, или положительное число — если первый аргумент больше второго. Ключевая функция — это вызываемый объект, который принимает один аргумент и возвращает другое значение, используемое в качестве ключа сортировки.

Пример:

sorted(iterable, key=cmp_to_key(locale.strcoll))  # locale-aware sort order

Примеры сортировки и краткое руководство по сортировке см. в разделе Приёмы сортировки.

Добавлено в версии 3.2.

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

Декоратор, оборачивающий функцию в вызываемый объект с мемоизацией, который сохраняет до maxsize последних вызовов. Он позволяет сэкономить время, если дорогостоящая функция или функция с интенсивными операциями ввода-вывода периодически вызывается с одинаковыми аргументами.

Кэш потокобезопасен, поэтому обёрнутую функцию можно использовать в нескольких потоках. Это означает, что базовая структура данных останется целостной при параллельных обновлениях.

Обёрнутая функция может быть вызвана более одного раза, если другой поток выполнит дополнительный вызов до завершения и кэширования первоначального вызова.

Поскольку для кэширования результатов используется словарь, позиционные и именованные аргументы функции должны быть хешируемыми.

Разные комбинации аргументов могут считаться отдельными вызовами с отдельными записями в кэше. Например, f(a=1, b=2) и f(b=2, a=1) различаются порядком именованных аргументов и могут занимать две отдельные записи в кэше.

Если задан user_function, он должен быть вызываемым объектом. Это позволяет применять декоратор lru_cache непосредственно к пользовательской функции, оставляя для maxsize значение по умолчанию 128:

@lru_cache
def count_vowels(sentence):
    return sum(sentence.count(vowel) for vowel in 'AEIOUaeiou')

Если для maxsize задано значение None, функция LRU отключается, и кэш может расти без ограничений.

Если для typed задано значение true, аргументы функции разных типов кэшируются отдельно. Если для typed задано значение false, реализация обычно считает их эквивалентными вызовами и кэширует только один результат. (Некоторые типы, например str и int, могут кэшироваться отдельно, даже если для typed задано значение false.)

Обратите внимание: различия типов учитываются только для непосредственных аргументов функции, а не для их содержимого. Скалярные аргументы Decimal(42) и Fraction(42) считаются разными вызовами с разными результатами. Напротив, кортежные аргументы ('answer', Decimal(42)) и ('answer', Fraction(42)) считаются эквивалентными.

К обёрнутой функции добавляется функция cache_parameters(), возвращающая новый dict со значениями maxsize и typed. Она предназначена только для получения информации. Изменение значений не оказывает никакого эффекта.

Чтобы оценить эффективность кэша и настроить параметр maxsize, к обёрнутой функции добавляется функция cache_info(), возвращающая именованный кортеж со значениями hits, misses, maxsize и currsize.

Декоратор также предоставляет функцию cache_clear() для очистки или аннулирования кэша.

Исходная функция доступна через атрибут __wrapped__. Это полезно для интроспекции, обхода кэша или повторного оборачивания функции с другим кэшем.

Кэш хранит ссылки на аргументы и возвращаемые значения до тех пор, пока они не устареют в кэше или пока кэш не будет очищен.

Если кэшируется метод, в кэш включается аргумент экземпляра self. См. раздел Как кэшировать вызовы методов?

Кэш LRU (по принципу «давно не использовался») лучше всего работает, когда последние вызовы позволяют лучше всего предсказать будущие (например, самые популярные статьи на новостном сервере обычно меняются каждый день). Ограничение размера кэша гарантирует, что он не будет расти бесконечно в долго работающих процессах, таких как веб-серверы.

Как правило, кэш LRU следует использовать только тогда, когда требуется повторно использовать ранее вычисленные значения. Поэтому нет смысла кэшировать функции с побочными эффектами, функции, которые при каждом вызове должны создавать отдельные изменяемые объекты (например, генераторы и асинхронные функции), или функции, результат которых не определяется только аргументами, например time() или random().

Пример кэша LRU для статического веб-содержимого:

@lru_cache(maxsize=32)
def get_pep(num):
    'Retrieve text of a Python Enhancement Proposal'
    resource = f'https://peps.python.org/pep-{num:04d}'
    try:
        with urllib.request.urlopen(resource) as s:
            return s.read()
    except urllib.error.HTTPError:
        return 'Not Found'

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

>>> get_pep.cache_info()
CacheInfo(hits=3, misses=8, maxsize=32, currsize=8)

Пример эффективного вычисления чисел Фибоначчи с помощью кэша для реализации метода динамического программирования:

@lru_cache(maxsize=None)
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

>>> [fib(n) for n in range(16)]
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610]

>>> fib.cache_info()
CacheInfo(hits=28, misses=16, maxsize=None, currsize=16)

Добавлено в версии 3.2.

Изменено в версии 3.3: Добавлен параметр typed.

Изменено в версии 3.8: Добавлен параметр user_function.

Изменено в версии 3.9: Добавлена функция cache_parameters()

@functools.total_ordering

Если класс определяет один или несколько методов расширенного сравнения, задающих порядок, этот декоратор класса добавляет остальные. Это упрощает определение всех возможных операций расширенного сравнения:

Класс должен определять один из методов __lt__(), __le__(), __gt__() или __ge__(). Кроме того, классу следует предоставить метод __eq__().

Например:

@total_ordering
class Student:
    def _is_valid_operand(self, other):
        return (hasattr(other, "lastname") and
                hasattr(other, "firstname"))
    def __eq__(self, other):
        if not self._is_valid_operand(other):
            return NotImplemented
        return ((self.lastname.lower(), self.firstname.lower()) ==
                (other.lastname.lower(), other.firstname.lower()))
    def __lt__(self, other):
        if not self._is_valid_operand(other):
            return NotImplemented
        return ((self.lastname.lower(), self.firstname.lower()) <
                (other.lastname.lower(), other.firstname.lower()))

Примечание

Этот декоратор упрощает создание типов с корректным полным порядком, однако за это приходится платить снижением скорости выполнения и усложнением трассировок стека для производных методов сравнения. Если замеры производительности показывают, что это узкое место приложения, реализация всех шести методов расширенного сравнения, скорее всего, позволит легко повысить быстродействие.

Примечание

Этот декоратор не пытается переопределять методы, объявленные в самом классе или его суперклассах. Это означает, что если суперкласс определяет оператор сравнения, total_ordering не будет реализовывать его повторно, даже если исходный метод является абстрактным.

Добавлено в версии 3.2.

Изменено в версии 3.4: Теперь поддерживается возврат NotImplemented из базовой функции сравнения для типов, которые невозможно распознать.

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

Возвращает новый частичный объект, который при вызове ведёт себя как func, вызванная с позиционными аргументами args и именованными аргументами keywords. Если при вызове переданы дополнительные аргументы, они добавляются к args. Если переданы дополнительные именованные аргументы, они дополняют и переопределяют keywords. Приблизительный эквивалент:

def partial(func, /, *args, **keywords):
    def newfunc(*fargs, **fkeywords):
        newkeywords = {**keywords, **fkeywords}
        return func(*args, *fargs, **newkeywords)
    newfunc.func = func
    newfunc.args = args
    newfunc.keywords = keywords
    return newfunc

partial() используется для частичного применения функции, то есть «фиксирует» часть аргументов и/или именованных параметров функции, создавая новый объект с упрощённой сигнатурой. Например, partial() можно использовать для создания вызываемого объекта, который ведёт себя как функция int(), где аргумент base по умолчанию равен двум:

>>> from functools import partial
>>> basetwo = partial(int, base=2)
>>> basetwo.__doc__ = 'Convert base 2 string to an int.'
>>> basetwo('10010')
18
class functools.partialmethod(func, /, *args, **keywords)

Возвращает новый дескриптор partialmethod, который работает как partial, но предназначен для использования в определении метода, а не для непосредственного вызова.

func должен быть дескриптором или вызываемым объектом (объекты, являющиеся и тем и другим, например обычные функции, обрабатываются как дескрипторы).

Если func является дескриптором (например, обычной функцией Python, classmethod(), staticmethod(), abstractmethod() или другим экземпляром partialmethod), вызовы __get__ делегируются базовому дескриптору, и в результате возвращается подходящий частичный объект.

Если func является вызываемым объектом, не являющимся дескриптором, динамически создаётся подходящий связанный метод. При использовании в качестве метода он ведёт себя как обычная функция Python: аргумент self вставляется первым позиционным аргументом, даже перед аргументами args и keywords, переданными конструктору partialmethod.

Пример:

>>> class Cell:
...     def __init__(self):
...         self._alive = False
...     @property
...     def alive(self):
...         return self._alive
...     def set_state(self, state):
...         self._alive = bool(state)
...     set_alive = partialmethod(set_state, True)
...     set_dead = partialmethod(set_state, False)
...
>>> c = Cell()
>>> c.alive
False
>>> c.set_alive()
>>> c.alive
True

Добавлено в версии 3.4.

functools.reduce(function, iterable[, initializer])

Последовательно применяет function с двумя аргументами к элементам iterable слева направо, сводя итерируемый объект к одному значению. Например, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) вычисляет ((((1+2)+3)+4)+5). Левый аргумент, x, — это накопленное значение, а правый аргумент, y, — новое значение из iterable. Если указан необязательный initializer, он помещается перед элементами итерируемого объекта при вычислении и используется в качестве значения по умолчанию, если итерируемый объект пуст. Если initializer не задан и iterable содержит только один элемент, возвращается первый элемент.

Приблизительный эквивалент:

def reduce(function, iterable, initializer=None):
    it = iter(iterable)
    if initializer is None:
        value = next(it)
    else:
        value = initializer
    for element in it:
        value = function(value, element)
    return value

Итератор, выдающий все промежуточные значения, см. в разделе itertools.accumulate().

@functools.singledispatch

Преобразует функцию в обобщённую функцию с диспетчеризацией по одному аргументу.

Чтобы определить обобщённую функцию, примените к ней декоратор @singledispatch. Определяя функцию с помощью @singledispatch, учитывайте, что выбор реализации выполняется по типу первого аргумента:

>>> from functools import singledispatch
>>> @singledispatch
... def fun(arg, verbose=False):
...     if verbose:
...         print("Let me just say,", end=" ")
...     print(arg)

Чтобы добавить перегруженные реализации функции, используйте атрибут register() обобщённой функции, который можно использовать как декоратор. Для функций с аннотациями типов декоратор автоматически определяет тип первого аргумента:

>>> @fun.register
... def _(arg: int, verbose=False):
...     if verbose:
...         print("Strength in numbers, eh?", end=" ")
...     print(arg)
...
>>> @fun.register
... def _(arg: list, verbose=False):
...     if verbose:
...         print("Enumerate this:")
...     for i, elem in enumerate(arg):
...         print(i, elem)

Также можно использовать types.UnionType и typing.Union:

>>> @fun.register
... def _(arg: int | float, verbose=False):
...     if verbose:
...         print("Strength in numbers, eh?", end=" ")
...     print(arg)
...
>>> from typing import Union
>>> @fun.register
... def _(arg: Union[list, set], verbose=False):
...     if verbose:
...         print("Enumerate this:")
...     for i, elem in enumerate(arg):
...         print(i, elem)
...

В коде без аннотаций типов соответствующий тип можно явно передать самому декоратору:

>>> @fun.register(complex)
... def _(arg, verbose=False):
...     if verbose:
...         print("Better than complicated.", end=" ")
...     print(arg.real, arg.imag)
...

Если код выбирает реализацию по типу коллекции (например, list), но при этом требуется аннотировать тип элементов коллекции (например, list[int]), тип диспетчеризации следует явно передать самому декоратору, а аннотацию типа указать в определении функции:

>>> @fun.register(list)
... def _(arg: list[int], verbose=False):
...     if verbose:
...         print("Enumerate this:")
...     for i, elem in enumerate(arg):
...         print(i, elem)

Примечание

Во время выполнения функция выбирает реализацию для экземпляра списка независимо от типа элементов списка, то есть [1,2,3] будет обрабатываться так же, как ["foo", "bar", "baz"]. Аннотация в этом примере предназначена только для средств статической проверки типов и не влияет на выполнение.

Для регистрации лямбда-функций и уже существующих функций атрибут register() также можно использовать в функциональной форме:

>>> def nothing(arg, verbose=False):
...     print("Nothing.")
...
>>> fun.register(type(None), nothing)

Атрибут register() возвращает функцию без декоратора. Это позволяет комбинировать декораторы, использовать pickling и создавать отдельные модульные тесты для каждого варианта:

>>> @fun.register(float)
... @fun.register(Decimal)
... def fun_num(arg, verbose=False):
...     if verbose:
...         print("Half of your number:", end=" ")
...     print(arg / 2)
...
>>> fun_num is fun
False

При вызове обобщённая функция выбирает реализацию по типу первого аргумента:

>>> fun("Hello, world.")
Hello, world.
>>> fun("test.", verbose=True)
Let me just say, test.
>>> fun(42, verbose=True)
Strength in numbers, eh? 42
>>> fun(['spam', 'spam', 'eggs', 'spam'], verbose=True)
Enumerate this:
0 spam
1 spam
2 eggs
3 spam
>>> fun(None)
Nothing.
>>> fun(1.23)
0.615

Если для определённого типа нет зарегистрированной реализации, используется порядок разрешения методов, чтобы найти более общую реализацию. Исходная функция, декорированная с помощью @singledispatch, регистрируется для базового типа object, то есть используется, если не найдена более подходящая реализация.

Если реализация зарегистрирована для абстрактного базового класса, виртуальные подклассы этого базового класса также будут обрабатываться этой реализацией:

>>> from collections.abc import Mapping
>>> @fun.register
... def _(arg: Mapping, verbose=False):
...     if verbose:
...         print("Keys & Values")
...     for key, value in arg.items():
...         print(key, "=>", value)
...
>>> fun({"a": "b"})
a => b

Чтобы узнать, какую реализацию обобщённая функция выберет для заданного типа, используйте атрибут dispatch():

>>> fun.dispatch(float)
<function fun_num at 0x1035a2840>
>>> fun.dispatch(dict)    # note: default implementation
<function fun at 0x103fe0000>

Чтобы получить доступ ко всем зарегистрированным реализациям, используйте атрибут registry, доступный только для чтения:

>>> fun.registry.keys()
dict_keys([<class 'NoneType'>, <class 'int'>, <class 'object'>,
          <class 'decimal.Decimal'>, <class 'list'>,
          <class 'float'>])
>>> fun.registry[float]
<function fun_num at 0x1035a2840>
>>> fun.registry[object]
<function fun at 0x103fe0000>

Добавлено в версии 3.4.

Изменено в версии 3.7: Атрибут register() теперь поддерживает аннотации типов.

Изменено в версии 3.11: Атрибут register() теперь поддерживает types.UnionType и typing.Union в качестве аннотаций типов.

class functools.singledispatchmethod(func)

Преобразует метод в обобщённую функцию с диспетчеризацией по одному аргументу.

Чтобы определить обобщённый метод, примените к нему декоратор @singledispatchmethod. Определяя функцию с помощью @singledispatchmethod, учитывайте, что выбор реализации выполняется по типу первого аргумента, который не является self или cls:

class Negator:
    @singledispatchmethod
    def neg(self, arg):
        raise NotImplementedError("Cannot negate a")

    @neg.register
    def _(self, arg: int):
        return -arg

    @neg.register
    def _(self, arg: bool):
        return not arg

@singledispatchmethod можно комбинировать с другими декораторами, например с @classmethod. Обратите внимание: чтобы обеспечить dispatcher.register, singledispatchmethod должен быть самым внешним декоратором. Ниже приведён класс Negator, в котором методы neg привязаны к классу, а не к его экземпляру:

class Negator:
    @singledispatchmethod
    @classmethod
    def neg(cls, arg):
        raise NotImplementedError("Cannot negate a")

    @neg.register
    @classmethod
    def _(cls, arg: int):
        return -arg

    @neg.register
    @classmethod
    def _(cls, arg: bool):
        return not arg

Этот же подход можно использовать с другими похожими декораторами: @staticmethod, @abstractmethod и другими.

Добавлено в версии 3.8.

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

Обновляет функцию-обёртку, чтобы она выглядела как обёрнутая функция. Необязательные аргументы представляют собой кортежи, задающие, какие атрибуты исходной функции напрямую присваиваются соответствующим атрибутам функции-обёртки и какие атрибуты функции-обёртки обновляются соответствующими атрибутами исходной функции. Значения этих аргументов по умолчанию — константы уровня модуля WRAPPER_ASSIGNMENTS (которая присваивает функции-обёртке атрибуты __module__, __name__, __qualname__, __annotations__, __type_params__ и __doc__, строку документации) и WRAPPER_UPDATES (которая обновляет атрибут __dict__ функции-обёртки, то есть её словарь экземпляра).

Чтобы обеспечить доступ к исходной функции для интроспекции и других целей (например, для обхода декоратора кэширования, такого как lru_cache()), эта функция автоматически добавляет к обёртке атрибут __wrapped__, ссылающийся на обёрнутую функцию.

Эта функция предназначена главным образом для функций декораторов, которые оборачивают декорируемую функцию и возвращают обёртку. Если функция-обёртка не обновлена, метаданные возвращаемой функции будут отражать определение обёртки, а не исходной функции, что обычно мало полезно.

update_wrapper() можно использовать с вызываемыми объектами, не являющимися функциями. Любые атрибуты, указанные в assigned или updated, которые отсутствуют у оборачиваемого объекта, игнорируются (то есть функция не пытается задать их для функции-обёртки). Исключение AttributeError по-прежнему возникает, если у самой функции-обёртки отсутствует какой-либо атрибут, указанный в updated.

Изменено в версии 3.2: Атрибут __wrapped__ теперь добавляется автоматически. Атрибут __annotations__ теперь копируется по умолчанию. Отсутствие атрибутов больше не вызывает исключение AttributeError.

Изменено в версии 3.4: Атрибут __wrapped__ теперь всегда ссылается на обёрнутую функцию, даже если эта функция определяла атрибут __wrapped__. (см. bpo-17482)

Изменено в версии 3.12: Атрибут __type_params__ теперь копируется по умолчанию.

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

Это вспомогательная функция для вызова update_wrapper() в качестве декоратора при определении функции-обёртки. Она эквивалентна partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated). Например:

>>> from functools import wraps
>>> def my_decorator(f):
...     @wraps(f)
...     def wrapper(*args, **kwds):
...         print('Calling decorated function')
...         return f(*args, **kwds)
...     return wrapper
...
>>> @my_decorator
... def example():
...     """Docstring"""
...     print('Called example function')
...
>>> example()
Calling decorated function
Called example function
>>> example.__name__
'example'
>>> example.__doc__
'Docstring'

Без этого фабричного декоратора имя примера функции было бы 'wrapper', а строка документации исходной функции example() была бы потеряна.

Объекты partial

Объекты partial — это вызываемые объекты, созданные с помощью partial(). У них есть три атрибута только для чтения:

partial.func

Вызываемый объект или функция. Вызовы объекта partial перенаправляются объекту func с новыми аргументами и именованными аргументами.

partial.args

Начальные позиционные аргументы, которые будут добавлены перед позиционными аргументами, переданными при вызове объекта partial.

partial.keywords

Именованные аргументы, которые будут переданы при вызове объекта partial.

Объекты partial подобны объектам функций тем, что они вызываемы, поддерживают слабые ссылки и могут иметь атрибуты. Однако между ними есть важные различия. Например, атрибуты __name__ и function.__doc__ не создаются автоматически. Кроме того, объекты partial, определённые в классах, ведут себя как статические методы и не преобразуются в связанные методы при поиске атрибута экземпляра.

© 2001–2024 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.12/library/functools.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API