Предположим, у меня есть сложный класс с множеством методов. В частности, его можно вызвать через участника __call__
. Я хотел бы иметь объект из того же класса, но не вызываемый и вообще не имеющий __call__
, потому что какой-то инструмент самоанализа обнаружит его, а это нежелательно.
Очевидное решение — создать новый класс, скопировав код исходного и удалив __call__
. Но есть ли способ сделать это программно?
Я пошел из:
import copy
class A:
def __call__(self):
print(self, "CALLED")
B = copy.deepcopy(A) # for some reason it is not a real copy...
del B.__call__ # seems to work, but also removes `__call__` from A !!!
a=A()
a() # TypeError: 'A' is not callable
# this was the more surprising to me...
к:
class B(A):
def __getattribute__(self, attr):
if attr == "__call__":
raise AttributeError(attr)
return super().__getattribute__(attr)
b = B()
b.__call__ # raises attribute error, nice
b() # still calls A.__call__... WTF!
пройдя через множество попыток.
Есть ли способ? Как получить настоящую копию объекта класса (а не экземпляра) в Python?
Я проверил существующие вопросы и ответы, но, похоже, ни один из них не касается этой конкретной проблемы. Я также подумал об извлечении исходного кода, удалении метода __call__
из строки и его повторной оценке.
Мне нужно, чтобы у дочернего класса не было __call__
— то есть удаление родительского метода (в случае наследования). Вы не можете. Вот я и подумал, давай скопируем класс, удалим его __call__
и сделаем из него объект... Но наткнулся на неожиданное
«Почему-то» — по какой причине? Очень сложно предложить возможные решения, не понимая контекста и ограничений.
Хорошо, я удалил "по какой-то причине", отредактировал сообщение
Не могли бы вы пойти наоборот? Определите B
как базовый класс, определите A(B)
(A
наследуется от B
) и реализуйте его __call__
Просто удалить «по какой-то причине» на самом деле не то же самое, что указать причину.
Возможно, этот пост поможет stackoverflow.com/questions/9541025/how-to-copy-a-class
Вы можете использовать type()
для динамического создания нового класса без указанного метода:
def remove_method_from_class(cls, new_class_name: str, method_name: str):
attrs = {k: v for k, v in cls.__dict__.items() if k != method_name}
return type(new_class_name, cls.__bases__, attrs)
class A:
def __call__(self):
print(self, "CALLED")
def other_method(self):
print("Other method")
B = remove_method_from_class(A, "B", "__call__")
a = A()
a() # This should work
b = B()
# b() # This raises a TypeError because B is not callable
print(hasattr(b, '__call__')) # Prints False
print(hasattr(a, '__call__')) # Prints True
b.other_method() # This should still work
Вы можете использовать метакласс, который позволяет вам контролировать создание класса:
class Meta(type):
def __new__(cls, clsname, bases, dct):
if not clsname.startswith('A'):
dct.pop('__call__', None)
return super(Meta, cls).__new__(cls, clsname, bases, dct)
class A(metaclass=Meta):
def __call__(self):
print(f"__called__: {self.__class__.__name__}")
class B(metaclass=Meta):
pass
class C(metaclass=Meta):
pass
for _cls in [A, B, C]:
try:
_cls()()
except:
print(f"{_cls} has no __call__ method")
Вне:
__called__: A
<class '__main__.B'> has no __call__ method
<class '__main__.C'> has no __call__ method
Почему бы не использовать наследование?