【问题标题】:Implementing inplace operations for methods in a class为类中的方法实现就地操作
【发布时间】:2020-08-01 16:51:34
【问题描述】:

pandas 中,很多方法都有关键字参数inplace。这意味着如果inplace=True,则调用的函数将在对象本身上执行,并返回None,另一方面,如果inplace=False,原始对象将保持不变,并在返回的新实例上执行该方法。我已经设法实现了这个功能,如下所示:

from copy import copy

class Dummy:
    def __init__(self, x: int):
        self.x = x

    def increment_by(self, increment: int, inplace=True):
        if inplace:
            self.x += increment
        else:
            obj = copy(self)
            obj.increment_by(increment=increment, inplace=True)
            return obj

    def __copy__(self):
        cls = self.__class__
        klass = cls.__new__(cls)
        klass.__dict__.update(self.__dict__)
        return klass
    
if __name__ == "__main__":
    a = Dummy(1)
    a.increment_by(1)
    assert a.x == 2
    b = a.increment_by(2, inplace=False)
    assert a.x == 2
    assert b.x == 4

它按预期工作。但是我有很多方法可以重复相同的模板:

def function(self, inplace=True, **kwds)
    if inplace:
        # do something
    else:
        obj = copy(self)
        obj.function(inplace=True, *args, **kwds)
        return obj

为了避免重复,我想创建一个装饰器和标记函数,它们可以就地执行,也可以非就地执行。 我想这样使用它

from copy import copy

class Dummy:
    def __init__(self, x: int):
        self.x = x

    @inplacify
    def increment_by(self, increment: int):
        self.x += increment # just the regular inplace way

    def __copy__(self):
        cls = self.__class__
        klass = cls.__new__(cls)
        klass.__dict__.update(self.__dict__)
        return klass

我希望它的行为与上面的示例相同。 我试过写不同的装饰器

(像这样开始的东西

def inplacify(method):
    def inner(self, *method_args, **method_kwds):
        inplace = method_kwds.pop("inplace", True)
        def new_method(inplace, *method_args, **method_kwds):

)

但我每次都卡住了。我需要self 的引用才能返回我没有的类的副本。使用装饰器更改函数签名也感觉有点模糊。我有几个问题:这种行为可以实现吗?我需要班级装饰师吗?这是否被认为是一种不好的做法,如果是,那么处理此类问题的最佳选择是什么?

【问题讨论】:

  • 我认为如果你的原始函数在完成任何操作后返回 self ,这是可能的,否则无法访问 obj 并在装饰器中返回它。

标签: python decorator in-place


【解决方案1】:

如果您的方法有return self,则以下工作:

import copy

def inplacify(method):
    def wrap(self,*a,**k):
        inplace = k.pop("inplace",True)
        if inplace:
            method(self,*a,**k)
        else:
            return method(copy.copy(self),*a,**k)
    return wrap

class classy:
    def __init__(self,n):
        self.n=n

    @inplacify
    def func(self,val):
        self.n+=val
        return self

我测试过:

inst = classy(5)
print(inst.n)
inst.func(4)
print(inst.n)
obj = inst.func(3,inplace=False)
print(inst.n,obj.n)

并且得到了预期的结果:

5
9
9 12

希望这能满足您的需求。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-01-01
    • 1970-01-01
    • 2016-04-06
    • 2019-03-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多