Не могу использовать partial как str [дубликат]
На этот вопрос уже есть ответ здесь:
- functools.частичный метод класса 1 ответ
from collections import namedtuple
Position = namedtuple('Position', 'x y')
Vector = namedtuple('Vector', 'x y')
Size = namedtuple('Size', 'width height')
Я хочу отформатировать числа с плавающей запятой при печати, потому что результат:
import math
print(Position(math.pi, math.pi), Vector(math.pi, math.pi), Size(math.pi, math.pi))
Слишком длинно:
Position(x=3.141592653589793, y=3.141592653589793) Vector(x=3.141592653589793, y=3.141592653589793) Size(width=3.141592653589793, height=3.141592653589793)
Поэтому я создал функцию для вывода именованных кортежей:
def pretty_float_pair(name, labels, obj):
"""
If labels = ('a', 'b') and object = (1.2345, 1.2345) returns:
'name(a=1.23, b=1.23)'
"""
return '{}({}={:.2f}, {}={:.2f})'.format(name, labels[0], obj[0], labels[1], obj[1])
Имя и метки должны быть фиксированы для каждого типа, и только аргумент obj изменяется, поэтому я подумал, что могу использовать functools partial.
from functools import partial
Position.__str__ = partial(pretty_float_pair, 'Position', ('x', 'y'))
Vector.__str__ = partial(pretty_float_pair, 'Vector', ('x', 'y'))
Size.__str__ = partial(pretty_float_pair, 'Size', ('width', 'height'))
print(Position(math.pi, math.pi), Vector(math.pi, math.pi), Size(math.pi, math.pi))
Но это бросает TypeError: pretty_float_pair() missing 1 required positional argument: 'obj'.
Удивительно, если я использую лямбду для создания функций, она работает.
Position.__str__ = lambda x: pretty_float_pair('Position', ('x', 'y'), x)
Vector.__str__ = lambda x: pretty_float_pair('Vector', ('x', 'y'), x)
Size.__str__ = lambda x: pretty_float_pair('Size', ('width', 'height'), x)
print(Position(math.pi, math.pi), Vector(math.pi, math.pi), Size(math.pi, math.pi))
Печатает то, что я хотел:
Position(x=3.14, y=3.14) Vector(x=3.14, y=3.14) Size(width=3.14, height=3.14)
Я пытаюсь понять, почему частичная версия не работает. работа.
2 ответа:
Поскольку лямбда ведет себя точно так же, как регулярная функция, определенная с помощью
functools.partial
возвращает не вызываемый дескриптор, грубо эквивалентный несвязанному методу. Это означает, что ему не передается параметрself
, который согласуется с ошибкой, которую вы видите.def
, она фактически является дескриптором. Метод__get__
лямбды возвращает связанную версию, которая передается в экземпляре какx
.Чтобы получить частичную функцию, которая ведет себя больше как метод, используйте
functools.partialmethod
вместо этого. Вам нужно будет переместитьobj
в начало списка аргументов, чтобы он мог получитьself
, когда метод привязан.Вот ваш пример:
from functools import partialmethod def pretty_float_pair(obj, name, labels): """ If labels = ('a', 'b') and object = (1.2345, 1.2345), returns: name(a=1.23, b=1.23) """ return '{}({}={:.2f}, {}={:.2f})'.format(name, labels[0], obj[0], labels[1], obj[1]) Position.__str__ = partialmethod(pretty_float_pair, 'Position', ('x', 'y')) Vector.__str__ = partialmethod(pretty_float_pair, 'Vector', ('x', 'y')) Size.__str__ = partialmethod(pretty_float_pair, 'Size', ('width', 'height')) print(Position(math.pi, math.pi), Vector(math.pi, math.pi), Size(math.pi, math.pi))
Функции
Получают свой неявный аргумент
self
, будучи дескрипторами : lookupx.f
конструирует и возвращает объект метода, который запоминаетx
, чтобы предоставить егоf
.functools.partial(...)
не возвращает дескриптор, поэтому он не получает специального обращения. (На самом деле это класс, поэтому он "возвращает" экземпляр самого себя.)