Suppose I have a complicated class with lots of methods. In particular it is callable via a __call__
member. I would like to have an object from the exact same class, but not callable and not with __call__
at all in fact, because some introspection tool would detect it and this is not wanted.
The obvious solution is to make a new class, copying the code from the original one, and removing __call__
. But is there a way to do this programmatically?
I went from:
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...
to:
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!
passing through many attempts.
Is there a way? How to get a true copy of a class object (not an instance) in Python?
I checked existing Q&A, but none seem to address this particular issue. I also thought about extracting source code, and removing the __call__
method from the string and having it re-evaluated.
You can use type()
to dynamically create a new class without the specified method:
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