Описание
Мой вопрос очень похож на вопрос, поднятый здесь , с одним отличием: я не хочу напрямую редактировать или заменять что-либо в исходном экземпляре класса.
Итак, представьте, что у нас есть класс, определенный как
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)
Это решение в основном делает все, что я хочу, за исключением того, что оно напрямую заменяет b()
-метод в предоставленном class1_obj
новым определением.
Следовательно, после вызова функции get_new_Class1_obj()
исходный код class1_obj
больше не может использоваться в исходном состоянии.
Это проблема, так как для приложения, которое у меня есть, я на самом деле требую, чтобы оригинальный метод b()
был по-прежнему доступен (я просто не использовал такую вещь в этом примере, чтобы сделать его немного проще).
Другой способ, которым я пытался это сделать, заключался в использовании фабрики классов (я пробовал несколько разных версий, причем ниже, вероятно, ближе всего к тому, что я хочу получить):
# Define function that returns new Class1 object
def get_new_Class1_obj(class1_obj):
# Define list with all methods that are overridden
override_attrs = ['__init__', '__getattribute__', '__dir__', 'b']
# Make a subclass of the class used for class1_obj
# This is required for functions like 'isinstance'
class new_Class1(class1_obj.__class__, object):
# Empty __init__ as no initialization is required
def __init__(self):
pass
# Make sure only overridden attributes are used from this class
def __getattribute__(self, name):
if name in override_attrs:
return(super().__getattribute__(name))
else:
return(getattr(class1_obj, name))
# Use the __dir__ of class1_obj
def __dir__(self):
return(dir(class1_obj))
# Override b-method
def b(self):
return("This is the new Class1 b()-method!")
# Initialize new_Class1
new_class1_obj = new_Class1()
# Return it
return(new_class1_obj)
Это также очень близко к тому, что я хочу (даже если постоянно обновлять список override_attrs
), и теперь я могу использовать оригинальный class1_obj
в new_class1_obj
, если захочу.
Однако проблема здесь в том, что bb()
-метод new_class1_obj
не будет работать должным образом, так как он будет использовать b()
-метод class1_obj
, а не метод new_class1_obj
.
Насколько я знаю, это невозможно сделать, не зная, что метод bb()
существует в такой форме.
Поскольку возможно, что кто-то подклассов Class1
и вводит c()
-метод, который вызывает b()
, это решение не будет работать должным образом (в то время как это будет работать должным образом с первым решением).
Отсутствие подкласса Class1
избавит вас от некоторых неприятностей, но это также будет означать, что такие функции, как isinstance
, больше не работают должным образом, не решая проблему с bb()
-методом.
Решение
Итак, в настоящее время я не могу придумать решение для этого (я пытался в течение нескольких дней).
Я подумываю об использовании решения с первой попытки, но вместо немедленной замены b()
-метода я сначала назначаю b()
что-то вроде _old_b()
или _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>
# Use with expected outputs
>>> class1_obj = Class1('TEST')
>>> new_class1_obj = get_new_Class1_obj(class1_obj)
>>> class1_obj is new_class1_obj
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 = 'TEST2'
>>> class1_obj.name
'TEST2'
>>> new_class1_obj.name
'TEST2'