自定义Python对象作为函数参数的处理方式?

自定义Python对象作为函数参数的处理方式?,python,Python,Python类的\uuuuu调用方法允许我们指定类成员作为函数的行为方式。我们可以做“相反的”吗,即指定类成员作为任意其他函数的参数的行为方式 作为一个简单的例子,假设我有一个包装列表的ListWrapper类,当我对该类的成员调用函数f时,我希望f映射到包装列表上。例如: x = WrappedList([1, 2, 3]) print(x + 1) # should be WrappedList([2, 3, 4]) d = {1: "a", 2: "b", 3:"c"} print(d[

Python类的
\uuuuu调用
方法允许我们指定类成员作为函数的行为方式。我们可以做“相反的”吗,即指定类成员作为任意其他函数的参数的行为方式

作为一个简单的例子,假设我有一个包装列表的
ListWrapper
类,当我对该类的成员调用函数
f
时,我希望
f
映射到包装列表上。例如:

x = WrappedList([1, 2, 3])
print(x + 1) # should be WrappedList([2, 3, 4])

d = {1: "a", 2: "b", 3:"c"}
print(d[x]) # should be WrappedList(["a", "b", "c"])
调用我正在寻找的假设的
\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
模拟,我们可以想象如下:

class WrappedList(object):
    def __init__(self, to_wrap):
        self.wrapped = to_wrap

   def __arg__(self, func):
       return WrappedList(map(func, self.wrapped))

现在,我知道(1)
\uuuuu arg\uuuu
不以这种形式存在,(2)在这个简单的示例中,不需要任何技巧就可以很容易地获得行为。但是,有没有一种方法可以近似我在一般情况下寻找的行为?

一般来说,你不能这样做*


您可以对大多数内置运算符(如
+
示例)和少数内置函数(如
abs
)执行等效操作。它们是通过对操作数调用特殊方法实现的,如中所述

当然,这意味着要为每个类型编写一大堆特殊方法,但要编写一个在一个地方实现所有这些特殊方法的基类(或decorator或元类,如果这不符合您的设计的话)并不难,方法是调用子类的
\uu arg\uu
,然后执行默认操作:

class ArgyBase:
    def __add__(self, other):
        return self.__arg__() + other
    def __radd__(self, other):
        return other + self.__arg__()
    # ... and so on

如果你想把它扩展到你自己创建的一整套函数,你可以给它们所有类似的特殊方法协议,类似于内置协议,并扩展你的基类来覆盖它们。或者,您可以将其短路,并在这些函数中直接使用
\uuuu arg\uuu
协议。为了避免大量重复,我会用一个装饰师来完成

def argify(func):
    def _arg(arg):
        try:
            return arg.__arg__()
        except AttributeError:
            return arg
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        args = map(_arg, args)
        kwargs = {kw: _arg(arg) for arg in args}
        return func(*args, **kwargs)
    return wrapper

@argify
def spam(a, b):
    return a + 2 * b

如果你真的想,你可以去包装其他人的功能:

sin = argify(math.sin)
…甚至是修补他们的模块:

requests.get = argify(requests.get)
…或者在早期版本的
gevent
中动态地修补整个模块,但我甚至不打算说明这一点,因为在这一点上,我们进入了“不做这个”的多原因领域

您在评论中提到,如果可能的话,您希望对其他人的一组函数执行此操作,而不必事先指定它们。这是否意味着在您导入的任何模块中构造的每个函数?如果你愿意创建一个导入钩子,你甚至可以这样做,但这似乎是一个更糟糕的主意。解释如何编写一个导入钩子,或者AST修补每个函数创建节点,或者在字节码周围插入包装器等等,这在这里是很难理解的,但是如果你的研究能力超出了你的常识,你可以找到答案。:)


作为旁注,如果我这样做,我不会调用方法
\uuuu arg\uuuu
,我会将其称为
arg
\u arg
。除了保留供语言将来使用之外,dunder方法样式还暗示了此处不正确的内容(特殊方法查找而不是普通调用,您可以在文档中搜索它,等等)


*有可能的语言,如C++,其中隐式转换和类型化变量的组合而不是类型化值意味着可以通过给它们一个带有隐含转换操作符的奇数类型来获得对象上调用的方法。

< P>一般不能这样做。*< /P>
您可以对大多数内置运算符(如
+
示例)和少数内置函数(如
abs
)执行等效操作。它们是通过对操作数调用特殊方法实现的,如中所述

当然,这意味着要为每个类型编写一大堆特殊方法,但要编写一个在一个地方实现所有这些特殊方法的基类(或decorator或元类,如果这不符合您的设计的话)并不难,方法是调用子类的
\uu arg\uu
,然后执行默认操作:

class ArgyBase:
    def __add__(self, other):
        return self.__arg__() + other
    def __radd__(self, other):
        return other + self.__arg__()
    # ... and so on

如果你想把它扩展到你自己创建的一整套函数,你可以给它们所有类似的特殊方法协议,类似于内置协议,并扩展你的基类来覆盖它们。或者,您可以将其短路,并在这些函数中直接使用
\uuuu arg\uuu
协议。为了避免大量重复,我会用一个装饰师来完成

def argify(func):
    def _arg(arg):
        try:
            return arg.__arg__()
        except AttributeError:
            return arg
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        args = map(_arg, args)
        kwargs = {kw: _arg(arg) for arg in args}
        return func(*args, **kwargs)
    return wrapper

@argify
def spam(a, b):
    return a + 2 * b

如果你真的想,你可以去包装其他人的功能:

sin = argify(math.sin)
…甚至是修补他们的模块:

requests.get = argify(requests.get)
…或者在早期版本的
gevent
中动态地修补整个模块,但我甚至不打算说明这一点,因为在这一点上,我们进入了“不做这个”的多原因领域

您在评论中提到,如果可能的话,您希望对其他人的一组函数执行此操作,而不必事先指定它们。这是否意味着在您导入的任何模块中构造的每个函数?如果你愿意创建一个导入钩子,你甚至可以这样做,但这似乎是一个更糟糕的主意。解释如何编写一个导入钩子,或者AST修补每个函数创建节点,或者在字节码周围插入包装器等等,这在这里是很难理解的,但是如果你的研究能力超出了你的常识,你可以找到答案。:)


作为旁注,如果我这样做,我不会调用方法
\uuuu arg\uuuu
,我会将其称为
arg
\u arg
。除了保留供语言将来使用之外,dunder方法样式还暗示了此处不正确的内容(特殊方法查找而不是普通调用,您可以在文档中搜索它,等等)

< >可以有语言,比如C++,其中有