如何在Python中重写实例级的实例方法而不替换它? 描述

如何在Python中重写实例级的实例方法而不替换它? 描述,python,methods,overriding,instance,Python,Methods,Overriding,Instance,我的问题与提出的问题非常相似,但有一个区别:我不想直接编辑或替换原始类实例中的任何内容 所以,基本上,假设我们有一个定义为 class Class1(object): """ This is Class1. """ def __init__(self, name): self.name = name def a(self): "Method a" return("This is the Class1 a

我的问题与提出的问题非常相似,但有一个区别:我不想直接编辑或替换原始类实例中的任何内容

所以,基本上,假设我们有一个定义为

class Class1(object):
    """
    This is Class1.

    """

    def __init__(self, name):
        self.name = name

    def a(self):
        "Method a"
        return("This is the Class1 a()-method!")

    def b(self):
        "Method b"
        return("This is the Class1 b()-method!")

    def bb(self):
        "Method bb"
        return(self.b())
现在我想做的是创建一个用户可以调用的函数,它为我提供一个
Class1
(或任何派生子类)的实例。 然后,我返回一个行为与提供的实例完全相同的对象,只是
b()
-方法被替换为

def b(self):
    return('This is the new Class1 b()-method!')
此对象的其他所有内容必须与提供的实例中的内容完全相同,以便新对象可以在旧对象可以使用的任何位置使用。 基本上就好像
Class1
(或任何使用过的子类)的定义已经使用了
b()
的这个定义

尝试 所以,我已经尝试了一些方法,但每一种方法都至少有一个我不喜欢的问题。 请注意,我省略了任何检查,以查看提供的对象是否确实是
Class1
类或任何派生子类的实例,因为它不会向我的描述中添加任何内容


我尝试使用给出的解决方案,得到以下结果:

from types import MethodType


# Define handy decorator for overriding instance methods
def override_method(instance):
    def do_override(method):
        setattr(instance, method.__name__, MethodType(method, instance))

    return(do_override)


# Define function that returns new Class1 object
def get_new_Class1_obj(class1_obj):
    # Override b-method
    @override_method(class1_obj)
    def b(self):
        return('This is the new Class1 b()-method!')

    # Return it
    return(class1_obj)
这个解决方案基本上实现了我想要的一切,只是它直接用新定义替换了所提供的
class1\u obj
中的
b()
-方法。 因此,一旦调用了
get_new_Class1_obj()
-函数,原始的
Class1_obj
就不能再在其原始状态下使用。 这是一个问题,对于我拥有的应用程序,我实际上要求原始的
b()
-方法仍然可用(在本例中,我只是没有使用这样的方法来保持它的简单性)


我尝试的另一种方法是使用类工厂(我已经尝试了几种不同的版本,下面可能是最接近我想要的):

这也非常接近我想要的(尽管不断更新
override\u attrs
列表很烦人),如果我想要的话,我现在可以在
新的\u class1\u obj
中使用原始的
class1\u obj
。 然而,这里的问题是
new_class1_obj
bb()
-方法不能正常工作,因为它将使用
class1_obj的
b()
-方法,而不是
new_class1_obj
的方法。 据我所知,如果不知道方法
bb()
以这样的形式存在,就无法强制执行此操作。 由于可能有人将
Class1
子类化,并引入调用
b()
c()
-方法,因此此解决方案将无法正常工作(而第一个解决方案可以正常工作)

在这里不子类化
Class1
可以消除一些麻烦,但这也意味着像
isinstance
这样的函数不再正常工作,而不能用
bb()
-方法解决问题

解决方案 因此,我目前无法找到解决方案(我已经尝试了几天)。 我正在考虑使用我的第一次尝试解决方案,但我没有立即替换
b()
-方法,而是首先将
b()
分配给
\u old\u b()
\u b()
(显然要确保它不存在),然后替换
b()
。 我真的不喜欢这个解决方案,因为它仍然让我觉得太粗糙和肮脏了

那么,有人对此有想法吗? 在我看来,这听起来像是一个非常简单的问题:我有一个实例,我想用一个新的实例方法更新它的一个实例方法,而不修改原始实例。 但是,这似乎并不是那么简单

例子 这方面的一个完整示例是:

# Define original Class1 class
class Class1(object):
    """
    This is Class1.

    """

    def __init__(self, name):
        self.name = name

    def a(self):
        "Method a"
        return("This is the Class1 a()-method!")

    def b(self):
        "Method b"
        return("This is the Class1 b()-method!")

    def bb(self):
        "Method bb"
        return(self.b())


# Define new b()-method
def b(self):
    # Return both old and new b() output
    return(class1_obj.b(), "This is the new Class1 b()-method!")


# Define function that returns new Class1 object
def get_new_Class1_obj(class1_obj):
    <code involving getting new_class1_obj>

我不是很确定你想要什么,但是下面这个合适吗?(文件test.py在python 3.7.3中测试正常)

此代码将返回:

$ python test.py
are insts same?  False
class1_obj.name? TEST
class1_obj.a(): This is the Class1 a()-method!
class1_obj.b(): This is the Class1 b()-method!
class1_obj.bb(): This is the Class1 b()-method!
new_class1_obj.name? TEST
new_class1_obj.a(): This is the Class1 a()-method!
new_class1_obj.b(): ('This is the Class1 b()-method!', 'This is the new Class1 b()-method!')
new_class1_obj.bb(): ('This is the Class1 b()-method!', 'This is the new Class1 b()-method!')
这是你想要的吗


我回顾了我的答案,在我看来,这一次接近你上面的例子。您对此有何看法?

我不知道这是否是您想要的,但下面的代码具有与您的示例相同的输出:

import copy

class ComplexObject:   # Equivalent of old Class1, but with no methods
    def __init__(self, name):
        self.name = name
        self.other_member = 'other, but could be intracomm'    

class Class1(object):
    def __init__(self, fwd):
        self.fwd = fwd  # All the interesting data is in this instance

    def get_name(self):
        return self.fwd.name  # Need to go through fwd to get at data
    def a(self):
        "Method a"
        return("This is the Class1 a()-method!")

    def b(self):
        "Method b"
        return("This is the Class1 b()-method!")

    def bb(self):
        "Method bb"
        return(self.b())

# Define function that returns new Class1 object
def get_new_Class1_obj(class1_obj):
    def b(self):
            # Return both old and new b() output
            return(class1_obj.b(), "This is the new Class1 b()-method!")
    new_instance = copy.copy(class1_obj)
    def b_wrapper():
        return b(new_instance)
    new_instance.b = b_wrapper

    return new_instance

complex=ComplexObject('TEST')
class1_obj = Class1(complex)
new_class1_obj = get_new_Class1_obj(class1_obj)
print("are insts same? ",class1_obj is new_class1_obj)
print("class1_obj.name",class1_obj.get_name())
print("class1_obj.a():",class1_obj.a())
print("class1_obj.b():",class1_obj.b())
print("class1_obj.bb():",class1_obj.bb())
print("new_class1_obj.name",new_class1_obj.get_name())
print("new_class1_obj.a():",new_class1_obj.a())
print("new_class1_obj.b():",new_class1_obj.b())
print("new_class1_obj.bb():",new_class1_obj.bb())
#Change some of the interesting data
class1_obj.fwd.name='FOO'
print("class1_obj.name",class1_obj.get_name())
print("new_class1_obj.name",new_class1_obj.get_name())
输出:

are insts same?  False
class1_obj.name TEST
class1_obj.a(): This is the Class1 a()-method!
class1_obj.b(): This is the Class1 b()-method!
class1_obj.bb(): This is the Class1 b()-method!
new_class1_obj.name TEST
new_class1_obj.a(): This is the Class1 a()-method!
new_class1_obj.b(): ('This is the Class1 b()-method!', 'This is the new Class1 b()-method!')
new_class1_obj.bb(): ('This is the Class1 b()-method!', 'This is the new Class1 b()-method!')
class1_obj.name FOO
new_class1_obj.name FOO
您可以看到,有趣数据中的更改会影响这两个类。

可能的解决方案 在查看了由给出的答案后,我提出了一个可能的解决方案,但我希望得到一些反馈:

from types import MethodType


# Define original Class1 class
class Class1(object):
    """
    This is Class1.

    """

    def __init__(self, name):
        self.name = name

    def a(self):
        "Method a"
        return("This is the Class1 a()-method!")

    def b(self):
        "Method b"
        return("This is the Class1 b()-method!")

    def bb(self):
        "Method bb"
        return(self.b())


# Define function that returns new Class1 object
def get_new_Class1_obj(class1_obj):
    # Obtain list of all properties that class1_obj has that are not methods
    props = [prop for prop in dir(class1_obj)
             if not isinstance(getattr(class1_obj, prop), MethodType)]

    # Make a subclass of the class used for class1_obj
    class new_Class1(class1_obj.__class__, object):
        # Empty __init__ as no initialization is required
        def __init__(self):
            pass

        # If requested attribute is not a method, use class1_obj for that
        def __getattribute__(self, name):
            if name in props:
                return(getattr(class1_obj, name))
            else:
                return(super().__getattribute__(name))

        # If requested attribute is not a method, use class1_obj for that
        def __setattr__(self, name, value):
            if name in props:
                setattr(class1_obj, name, value)
            else:
                super().__setattr__(name, value)

        # Use the __dir__ of class1_obj
        def __dir__(self):
            return(dir(class1_obj))

        # Define new b()-method
        def b(self):
            # Return both old and new b() output
            return(class1_obj.b(), "This is the new Class1 b()-method!")

    # Initialize new_Class1
    new_class1_obj = new_Class1()

    # Return it
    return(new_class1_obj)
这里的所有输出都是我想要的输出。 我仍然不确定是否希望
new_class1_obj
中的非方法更改影响
class1_obj
,但我可以通过简单地不重写
\uuuu setattr_uu()
轻松地删除它。 上述操作还确保向
new_class1_obj
添加新属性不会影响
class1_obj

如果我真的想让相反的方法发挥作用,我可以在
\uuuu getattribute\uuuuuuuu
中添加确定的
道具(在
class1\u obj
中添加一个新属性会影响
new\u class1\u obj
)。

在什么情况下/如何调用
class1\u obj
的原始
b()
函数?也许这会让我们对如何proceed@CodelessBugging例如,如果用户希望同时使用
class1\u obj
new\u class1\u obj
。但是,我还要求可以从
new\u class1\u obj
调用原始的
b()
-方法。编辑:我添加了一个示例。您的问题似乎源于
bb()
方法,它仍然需要在原始实例或新实例中调用旧的
b()
方法,但新实例上的任何外部调用程序都将重定向到新的
b()
。似乎您应该直接在类中对此进行建模。@quamrana不,我希望
bb()
-m
import copy

class ComplexObject:   # Equivalent of old Class1, but with no methods
    def __init__(self, name):
        self.name = name
        self.other_member = 'other, but could be intracomm'    

class Class1(object):
    def __init__(self, fwd):
        self.fwd = fwd  # All the interesting data is in this instance

    def get_name(self):
        return self.fwd.name  # Need to go through fwd to get at data
    def a(self):
        "Method a"
        return("This is the Class1 a()-method!")

    def b(self):
        "Method b"
        return("This is the Class1 b()-method!")

    def bb(self):
        "Method bb"
        return(self.b())

# Define function that returns new Class1 object
def get_new_Class1_obj(class1_obj):
    def b(self):
            # Return both old and new b() output
            return(class1_obj.b(), "This is the new Class1 b()-method!")
    new_instance = copy.copy(class1_obj)
    def b_wrapper():
        return b(new_instance)
    new_instance.b = b_wrapper

    return new_instance

complex=ComplexObject('TEST')
class1_obj = Class1(complex)
new_class1_obj = get_new_Class1_obj(class1_obj)
print("are insts same? ",class1_obj is new_class1_obj)
print("class1_obj.name",class1_obj.get_name())
print("class1_obj.a():",class1_obj.a())
print("class1_obj.b():",class1_obj.b())
print("class1_obj.bb():",class1_obj.bb())
print("new_class1_obj.name",new_class1_obj.get_name())
print("new_class1_obj.a():",new_class1_obj.a())
print("new_class1_obj.b():",new_class1_obj.b())
print("new_class1_obj.bb():",new_class1_obj.bb())
#Change some of the interesting data
class1_obj.fwd.name='FOO'
print("class1_obj.name",class1_obj.get_name())
print("new_class1_obj.name",new_class1_obj.get_name())
are insts same?  False
class1_obj.name TEST
class1_obj.a(): This is the Class1 a()-method!
class1_obj.b(): This is the Class1 b()-method!
class1_obj.bb(): This is the Class1 b()-method!
new_class1_obj.name TEST
new_class1_obj.a(): This is the Class1 a()-method!
new_class1_obj.b(): ('This is the Class1 b()-method!', 'This is the new Class1 b()-method!')
new_class1_obj.bb(): ('This is the Class1 b()-method!', 'This is the new Class1 b()-method!')
class1_obj.name FOO
new_class1_obj.name FOO
from types import MethodType


# Define original Class1 class
class Class1(object):
    """
    This is Class1.

    """

    def __init__(self, name):
        self.name = name

    def a(self):
        "Method a"
        return("This is the Class1 a()-method!")

    def b(self):
        "Method b"
        return("This is the Class1 b()-method!")

    def bb(self):
        "Method bb"
        return(self.b())


# Define function that returns new Class1 object
def get_new_Class1_obj(class1_obj):
    # Obtain list of all properties that class1_obj has that are not methods
    props = [prop for prop in dir(class1_obj)
             if not isinstance(getattr(class1_obj, prop), MethodType)]

    # Make a subclass of the class used for class1_obj
    class new_Class1(class1_obj.__class__, object):
        # Empty __init__ as no initialization is required
        def __init__(self):
            pass

        # If requested attribute is not a method, use class1_obj for that
        def __getattribute__(self, name):
            if name in props:
                return(getattr(class1_obj, name))
            else:
                return(super().__getattribute__(name))

        # If requested attribute is not a method, use class1_obj for that
        def __setattr__(self, name, value):
            if name in props:
                setattr(class1_obj, name, value)
            else:
                super().__setattr__(name, value)

        # Use the __dir__ of class1_obj
        def __dir__(self):
            return(dir(class1_obj))

        # Define new b()-method
        def b(self):
            # Return both old and new b() output
            return(class1_obj.b(), "This is the new Class1 b()-method!")

    # Initialize new_Class1
    new_class1_obj = new_Class1()

    # Return it
    return(new_class1_obj)
# Do testing
if __name__ == '__main__':
    # Initialize instances
    class1_obj = Class1('TEST')
    new_class1_obj = get_new_Class1_obj(class1_obj)

    # Check that instances are not the same
    print(class1_obj is new_class1_obj)

    # Check outputs of class1_obj
    print(class1_obj.name)
    print(class1_obj.a())
    print(class1_obj.b())
    print(class1_obj.bb())

    # Check outputs of new_class1_obj
    print(new_class1_obj.name)
    print(new_class1_obj.a())
    print(new_class1_obj.b())
    print(new_class1_obj.bb())

    # Check that non-method changes in class1_obj affect new_class1_obj
    class1_obj.name = 'TEST2'
    print(class1_obj.name)
    print(new_class1_obj.name)

    # Check that non-method changes in new_class1_obj affect class1_obj
    new_class1_obj.name = 'TEST3'
    print(class1_obj.name)
    print(new_class1_obj.name)