1

我真的很喜欢“魔术方法”的语法或者它们在 Python 中被调用的任何东西,比如

class foo:
    def __add__(self,other): #It can be called like c = a + b
        pass

通话

c = a + b

然后被翻译成

a.__add__(b)

是否可以为“非魔法”功能模仿这种行为?在数值计算中,我需要 Kronecker 产品,并且渴望拥有“kron”函数,这样

kron(a,b) 

实际上是

a.kron(b)?

用例是:我有两个相似的类,比如矩阵和向量,它们都有 Kronecker 积。我想打电话给他们

a = matrix()
b = matrix()
c = kron(a,b)

a = vector()
b = vector()
c = kron(a,b)

矩阵和向量类定义在一个 .py 文件中,因此共享公共命名空间。那么,实现上述功能的最佳(Pythonic?)方法是什么?可能的解决方案:

1) 拥有一个 kron() 函数并进行类型检查

2)有不同的命名空间

3) ?

4

4 回答 4

7

python 默认操作符方法(__add__等等)是硬连线的;python 会寻找它们,因为操作符实现会寻找它们。

然而,没有什么能阻止你定义一个kron做同样事情的函数。查找__kron____rkron__传递给它的对象:

def kron(a, b):
    if hasattr(a, '__kron__'):
        return a.__kron__(b)
    if hasattr(b, '__rkron__'):
        return b.__rkron__(a)
    # Default kron implementation here
    return complex_operation_on_a_and_b(a, b)
于 2012-07-09T08:01:13.737 回答
4

您所描述的是multiple dispatchmultimethods。魔术方法是实现它们的一种方法,但实际上更常见的是拥有一个可以注册特定类型实现的对象。

例如,http ://pypi.python.org/pypi/multimethod/会让你写

@multimethod(matrix, matrix)
def kron(lhs, rhs):
    pass

@multimethod(vector, vector)
def kron(lhs, rhs):
    pass

自己写一个多方法装饰器很容易;BDFL 在一篇文章中描述了一个典型的实现。这个想法是multimethod装饰器将类型签名和方法与注册表中的方法名称相关联,并用生成的方法替换该方法,该方法执行类型查找以找到最佳匹配。

于 2012-07-09T08:21:22.347 回答
3

从技术上讲,实现类似于“标准”运算符(以及类似运算符 - thinklen()等)的行为并不困难:

def kron(a, b):
    if hasattr(a, '__kron__'):
        return a.__kron__(b)
    elif hasattr(b, '__kron__'):
        return b.__kron__(a)
    else:
        raise TypeError("your error message here")

现在您只需要__kron__(self, other)在相关类型上添加一个方法(假设您可以控制这些类型,或者它们不使用插槽或其他任何会阻止在类语句主体之外添加方法的东西)。

现在我不会使用__magic__上面代码片段中的命名方案,因为这应该是为语言本身保留的。

另一种解决方案是维护一个type:specifici function映射并让“通用”kron函数查找映射,即:

# kron.py
from somewhere import Matrix, Vector

def matrix_kron(a, b):
    # code here

def vector_kron(a, b):
    # code here

KRON_IMPLEMENTATIONS = dict(
    Matrix=matrix_kron,
    Vector=vector_kron,
    )

def kron(a, b):
    for typ in (type(a), type(b)):
        implementation = KRON_IMPLEMENTATION.get(typ, None)
        if implementation:
            return implementation(a, b)
    else:
        raise TypeError("your message here")

该解决方案不适用于继承,但它“不那么令人惊讶” - 不需要猴子补丁或__magic__名称等。

于 2012-07-09T07:59:09.743 回答
0

我认为拥有一个委托实际计算的函数是一种很好的方法。如果 Kronecker 产品仅适用于两个相似的类,您甚至可以在函数中进行类型检查:

def kron(a, b):
    if type(a) != type(b):
        raise TypeError('expected two instances of the same class, got %s and %s'%(type(a), type(b)))
    return a._kron_(b)

然后,您只需要_kron_在该类上定义一个方法。这只是一些基本示例,您可能希望对其进行改进以更优雅地处理类没有_kron_方法或处理子类的情况。

标准库中的二进制操作通常具有反向对偶 ( __add__and __radd__),但由于您的运算符仅适用于相同类型的对象,因此在这里没有用。

于 2012-07-09T08:00:11.270 回答