Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/304.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python中的函数组合运算符_Python_Python Decorators_Function Composition - Fatal编程技术网

Python中的函数组合运算符

Python中的函数组合运算符,python,python-decorators,function-composition,Python,Python Decorators,Function Composition,在中,我询问了Python中的函数组合运算符。提供了执行此任务的以下代码 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.fu

在中,我询问了Python中的函数组合运算符。提供了执行此任务的以下代码

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))
通过这些添加,可以使用
@
*
作为运算符来组合函数。例如,可以编写
打印((add1@add2)(5),(add1*add2)(5),(add1>add2)(5))
并获取
#8
。(PyCharm抱怨布尔值不能调用
(add1>add2)(5)
,但它仍然运行。)

但一直以来,我都想使用
作为函数合成操作符。所以我补充说

def __getattribute__(self, other):
    return lambda *args, **kw: self.func(other.func(*args, **kw))
(请注意,这会弄脏
update\u wrapper
,为了解决此问题,可以将其移除。)

当我运行
print((add1.add2)(5))
时,我在运行时遇到以下错误:
AttributeError:'str'对象没有属性“func”
。显然,
\uuuuuGetAttribute\uuuuuu
的参数在传递到
\uuuuuuuGetAttribute\uuuuu
之前被转换成字符串


有没有办法解决这个问题?还是我把问题误诊了,其他方法也行?

你不能得到你想要的。
表示法不是二进制运算符,它是一个,只有值操作数(位于
的左侧)和标识符。是字符串,而不是生成值引用的完整表达式

从:

属性引用是主引用,后跟句点和名称:

attributeref ::=  primary "." identifier
主对象必须计算为支持属性引用的类型的对象,大多数对象都会这样做。然后要求该对象生成名称为标识符的属性

因此,在编译时,Python将
标识符
解析为字符串值,而不是表达式(这是操作数到运算符的结果)。
\uuuu getattribute\uuu
钩子(以及任何其他钩子)只需处理字符串。这是没有办法的;动态属性访问函数严格要求
name
必须是字符串:

>>> getattr(object(), 42)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: getattr(): attribute name must be string
getattr(object(),42) 回溯(最近一次呼叫最后一次): 文件“”,第1行,在 TypeError:getattr():属性名称必须是字符串
如果要使用语法组合两个对象,则只能使用二进制运算符,因此表达式只能使用两个操作数,并且只能使用具有挂钩的表达式(布尔值
操作符没有挂钩,因为它们的计算很慢,
不是
没有挂钩,因为它们操作对象标识,而不是对象值)。

我实际上不愿意提供这个答案。但是你应该知道,在某些情况下,你可以使用点“
”表示法,即使它是主要的。此解决方案仅适用于可以从
全局()访问的函数。

要测试:

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

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

print((add1.add2)(5))
# 8

通过使用
inspect
模块,您可以绕过只在全局范围内定义可组合函数的限制。请注意,这与Pythonic的功能相差甚远,使用
inspect
将使代码更难跟踪和调试。其思想是使用
inspect.stack()
从调用上下文获取名称空间,并在其中查找变量名

import functools
import inspect

class Composable:

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

    def __getattr__(self, othername):
        stack = inspect.stack()[1][0]
        other = stack.f_locals[othername]
        return Composable(lambda *args, **kw: self._func(other._func(*args, **kw)))

    def __call__(self, *args, **kw):
        return self._func(*args, **kw)
请注意,我将
func
更改为
\u func
,以防止与实际名为
func
的函数组合时发生冲突。此外,我将lambda包装在一个
可组合(…)
中,以便它本身可以组合

证明其在全球范围外工作:

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

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

    print((add1.add2)(5))

main()
# 8
这为您提供了一个隐式的好处,即可以将函数作为参数传递,而不必担心在全局范围中将变量名解析为实际函数名。例如:

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

def repeat(func, count):
    result = func
    for i in range(count-1):
        result = result.func
    return result
    
print(repeat(inc, 6)(5))
# 11

中的
other
是属性的名称,它不是像
@
那样的二进制运算符。正如您所看到的,
是为访问属性和方法保留的。但是您可以使用与原始运算符更相似的
°
,并且不会产生此问题。@FrenchMasterSword实现它的神奇方法是什么?!它不是一个有效的Python操作符。它不是一个Python操作符^^^'@Frenchmaster剑所以…这到底是一个有用的建议吗?你是在建议OP编写自己版本的解释器来作为操作符处理该符号吗?我一直在试图找到绕过此限制的方法,但是我想我必须放弃。
@
*
并没有那么糟糕。我在这里问了一个相关的问题:非常奇怪。但是非常感谢您不辞辛劳地提供这些信息。这只适用于中定义的模块中的全局变量。
Composable
。然后您可以开始从堆栈中提取名称,这仍然不能给您完整的表达式访问权限;这只是语法错误或按错误的顺序进行分析。@Russabbot不确定您为什么认为这很奇怪?您有一个字符串,并且
globals()
是模块名称空间映射字符串到对象的字典。因此,是的,如果您将自己限制在同一模块中的全局变量,则可以通过这种方式将属性名称映射到全局变量。这是一种间接路径,只是不要期望与二进制运算符挂钩具有相同的表达自由度。@Martijn Pieters。您是对的。”“怪异”是一个错误的词。我的反应是,这个解决方案只在有限的情况下有效——正如你也指出的。
@Composable
def inc(x):
    return x + 1

def repeat(func, count):
    result = func
    for i in range(count-1):
        result = result.func
    return result
    
print(repeat(inc, 6)(5))
# 11