【问题标题】:Dynamically add support for arithmetic magic function in a python class在 python 类中动态添加对算术魔术函数的支持
【发布时间】:2019-10-30 06:35:40
【问题描述】:

假设我有一个 python 类A:

class A:
    def __init__(self, matrix, metadata: list):
        self.matrix = np.array(matrix)
        self.metadata = metadata
    #... 

现在我希望所有算术运算都适用于我的班级。他们应该简单地将操作转换为matrix,即像这样:

    def __add__(self, other):
        if isinstance(other, type(self)):
            raise ValueError("Not allowed.")
        else:
            return A(
                matrix=self.matrix.__add__(other),
                metadata=self.metadata,
            )

现在的问题是我必须为每个算术魔术函数重复几乎相同的代码,即__add__, __sub__, __mul__, __truediv__, __pow__, __radd__, __rsub__, __rmul__, __rtruediv__, __iadd__, __isub__, __imul__, __itruediv__, __abs__, __round__, __floor__, __ceil__, __trunc__。这会导致大量重复代码。

如何在 for 循环中动态定义它们? 喜欢

magic_functions = ["__add__", "__sub__", ...]
for magic_function in magic_functions:
    # define the method and add it to the class

【问题讨论】:

  • 也许这个答案会对你有所帮助stackoverflow.com/questions/3431676/…
  • 我建议忘记循环,并明确定义和测试这些矩阵算术方法:下一个阅读您的代码的人会感谢您!
  • @Dan 谢谢你的链接。据我了解,这里不适用。
  • @ReblochonMasque 谢谢你的建议。我明白你的观点,它确实有道理。这就是我现在实现它的方式。另一方面,拥有大约 15 个几乎相同的功能有点违反 DRY 原则,并且添加一些小改动会很痛苦。
  • 同意,__add____sub__ 只有一个字符差异(+ i/o -);然而,这种差异非常重要,足以证明这些方法的两个完整实现是正确的。

标签: python python-3.x oop


【解决方案1】:

这种(广泛的)问题是operator 模块的目的:

import operator
def mkop(f):    # the usual scope for a closure
  def op(self,o):
    if isinstance(o,type(self)): raise …
    return type(self)(matrix=f(self.matrix,o),
                      metadata=self.metadata)
  return op
for op in ['add','sub',…]:
  setattr(A,"__%s__"%op,mkop(getattr(operator,op)))

您还可以在定义类时使用locals()[…]=mkop(…)(在其罕见的安全用途之一中)执行上述操作。

【讨论】:

    【解决方案2】:

    我想建议您在这种情况下使用decorator。可能这不是那么短,但您将节省代码的可读性。

    import numpy as np
    
    def decorator(fn):
        def ret_fn(*args, **kwargs):
            if isinstance(args[1], type(args[0])):
                raise ValueError("Not allowed.")
            else:
                return fn(*args, **kwargs)
    
        return ret_fn
    
    class A:
        def __init__(self, matrix, metadata: list):
            self.matrix = np.array(matrix)
            self.metadata = metadata
    
        @decorator
        def __add__(self, other):
            return A(
                matrix=self.matrix.__add__(othe),
                metadata=self.metadata,
            )
    

    结果:

    >>> a1 = A([[1], [2]], [])
    >>> a2 = a1 + [[3], [4]]
    >>> print(a2.matrix)
    [[4]
     [6]]
    >>> a1 + a1
    Traceback (most recent call last):
    ...
        raise ValueError("Not allowed.")
    ValueError: Not allowed.
    

    我不知道你的函数之间有多少区别,但你可以重写装饰器并且函数非常简约:

    装饰者

    def decorator(fn):
        def ret_fn(*args, **kwargs):
            if isinstance(args[1], type(args[0])):
                raise ValueError("Not allowed.")
            else:
                return A(
                        matrix=fn(*args, **kwargs),
                        metadata=args[0].metadata,
                    )
    
        return ret_fn
    

    方法

    @decorator
    def __add__(self, other):
        return self.matrix.__add__(other)
    

    【讨论】:

      猜你喜欢
      • 2022-09-24
      • 1970-01-01
      • 2019-11-16
      • 1970-01-01
      • 2018-10-25
      • 2023-03-07
      • 2012-01-21
      • 2021-12-07
      • 1970-01-01
      相关资源
      最近更新 更多