python中什么是组合_Python中的函数组合运算符

在这个问题中,我询问了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__之前已转换为字符串。

有没有办法解决这种转换? 还是我误诊了问题,还有其他方法可行吗?

__getattribute__中的other是属性的名称,它不是像>或@这样的二进制运算符。

如您所见,.被保留用于访问属性和方法。 但是您可以使用,它与原始运算符更相似,并且不会产生此问题。

@FrenchMasterSword实现该功能的神奇方法是什么?! 它不是有效的Python运算符。

好吧,它不是python运算符^^

@FrenchMasterSword那么...这个建议到底有多准确? 您是否建议OP编写自己的解释器版本以将该符号作为运算符处理?

您可能需要__getattr__,而不是__getattribute__。 仅当属性不存在时才调用前者,而对所有属性都调用后者。

@MartijnPieters仍然会得到字符串add2而不是Composable对象。

@jonrsharpe:是的,很好。 __getattr__只能处理属性名称。

相关:如何在python中乘法函数?

你没有想要的东西。 .表示符不是二进制运算符,它是一个主要运算符,只有值操作数(.的左侧)和一个标识符。标识符是字符串,不是生成对值的引用的成熟表达式。

在"属性引用"部分中:

An attribute reference is a primary followed by a period and a name:

attributeref ::=  primary"." identifier

The primary must evaluate to an object of a type that supports attribute references, which most objects do. This object is then asked to produce the attribute whose name is the identifier.

因此,在编译时,Python将identifier解析为一个字符串值,而不是一个表达式(这是为运算符提供给运算符的结果)。 __getattribute__挂钩(以及其他任何属性访问挂钩)仅需要处理字符串。没有办法解决这个问题。动态属性访问函数getattr()严格要求name必须是字符串:

>>> getattr(object(), 42)

Traceback (most recent call last):

File"", line 1, in

TypeError: getattr(): attribute name must be string

如果要使用语法来组成两个对象,则只能使用二元运算符,因此需要两个操作数的表达式以及只有具有钩子的表达式(布尔值and和or运算符没有钩子,因为它们懒惰地求值,is和is not没有钩子,因为它们对对象标识(而不是对象值)进行操作。

我一直在努力寻找解决此限制的方法,但我想必须放弃。 @和*还不错。

我在这里问了一个相关的问题:stackoverflow.com/questions/54164382/

通过使用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的函数编写函数,则将func更改为_func以防止冲突。另外,我将您的lambda包裹在Composable(...)中,以便它本身可以组成。

证明它在全局范围之外起作用:

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

我实际上不愿意提供这个答案。但是您应该知道,在某些情况下,即使它是主要的,也可以使用点" ."表示法。此解决方案仅适用于可以从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

很奇怪。但是非常感谢您提供这些信息的麻烦。

这仅适用于在模块Composable中定义的全局变量。虽然您随后可以开始从堆栈中提取名称,但这仍然不能为您提供完整的表达式访问权限;这些将仅仅是语法错误或以错误的顺序进行解析。

@RussAbbott不知道您为什么认为这很奇怪?您有一个字符串,而globals()是将字符串映射到对象的模块名称空间的字典。所以是的,如果您在同一模块中将自己限制为全局变量,则可以通过这种方式将属性名称映射到全局变量。那是一条间接的道路,只是不要指望拥有与二元运算符的钩子一样的表达自由。

@Martijn Pieters。你是对的。"怪异"是错误的词。我对此的反应是,该解决方案仅在有限的情况下有效-正如您还指出的那样。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值