Оператор композиции функций в Python - PullRequest
0 голосов
/ 12 января 2019

В этот вопрос Я задал вопрос об операторе композиции функций в Python. @ Philip Tzou предложил следующий код, который выполняет эту работу.

import functools

class Composable:

    def __init__(self, func):
        self.func = func
        functools.update_wrapper(self, func)

    def __matmul__(self, other):
        return lambda *args, **kw: self.func(other.func(*args, **kw))

    def __call__(self, *args, **kw):
        return self.func(*args, **kw)

Я добавил следующие функции.

def __mul__(self, other):
    return lambda *args, **kw: self.func(other.func(*args, **kw))

def __gt__(self, other):
    return lambda *args, **kw: self.func(other.func(*args, **kw))

С этими дополнениями можно использовать @, * и > в качестве операторов для составления функций. Например, можно написать print((add1 @ add2)(5), (add1 * add2)(5), (add1 > add2)(5)) и получить # 8 8 8. (PyCharm жалуется, что логическое значение не вызывается для (add1 > add2)(5). Но оно все еще работает.)

Тем не менее, я хотел использовать . в качестве оператора композиции функций. Поэтому я добавил

def __getattribute__(self, other):
    return lambda *args, **kw: self.func(other.func(*args, **kw))

(Обратите внимание, что это нарушает update_wrapper, что может быть удалено ради этого вопроса.)

Когда я запускаю print((add1 . add2)(5)), я получаю эту ошибку во время выполнения: AttributeError: 'str' object has no attribute 'func'. Оказывается (очевидно), что аргументы __getattribute__ преобразуются в строки перед передачей в __getattribute__.

Есть ли способ обойти это преобразование? Или я неправильно диагностирую проблему, и какой-то другой подход будет работать?

Ответы [ 2 ]

0 голосов
/ 14 января 2019

Я на самом деле не желаю давать этот ответ. Но вы должны знать, что в определенных обстоятельствах вы можете использовать точечную нотацию ".", даже если она является основной. Это решение работает только для функций, доступ к которым можно получить с globals():

import functools

class Composable:

    def __init__(self, func):
        self.func = func
        functools.update_wrapper(self, func)

    def __getattr__(self, othername):
        other = globals()[othername]
        return lambda *args, **kw: self.func(other.func(*args, **kw))

    def __call__(self, *args, **kw):
        return self.func(*args, **kw)

Для проверки:

@Composable
def add1(x):
    return x + 1

@Composable
def add2(x):
    return x + 2

print((add1.add2)(5))
# 8
0 голосов
/ 12 января 2019

Вы не можете иметь то, что вы хотите. Обозначение .: , не двоичный оператор , это основной , только с операндом значения (левая часть .) и идентификатор . Идентификаторы - это строки символов, а не полноразмерные выражения, которые создают ссылки на значение.

Из Ссылки на атрибуты раздел :

Ссылка на атрибут является первичной, за которой следует точка и имя:

attributeref ::=  primary "." identifier

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

Таким образом, при компиляции Python анализирует identifier как строковое значение, а не как выражение (что вы получаете для операндов операторов). Хук __getattribute__ (и любой другой хук доступа к атрибуту ) имеет дело только со строками. Обойти это невозможно; функция доступа к динамическим атрибутам getattr() строго следит за тем, чтобы name была строкой:

>>> getattr(object(), 42)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: getattr(): attribute name must be string

Если вы хотите использовать синтаксис для составления двух объектов, вы ограничены бинарными операторами, поэтому выражения, которые принимают два операнда, и только те, которые имеют хуки (булево and и or операторы не имеют хуков, потому что они лениво оценивают, is и is not не имеют хуков, потому что они работают с идентичностью объекта, а не со значениями объекта).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...