Running the code below raises the following exception:
ERROR!
Traceback (most recent call last):
File "<string>", line 22, in <module>
TypeError: MyDecorator.__init__() takes 2 positional arguments but 4 were given
>
When an object derived from class Subclass is instanciated, I want to access all the arguments given to the constructor, from my decorator's code. I had to stop before because my current code breaks the inheritance. Could someone steer me in the right direction?
class MyDecorator:
def __init__(self, original_class):
self.original_class = original_class
def __call__(self, *args, **kwargs):
instance = self.original_class(*args, **kwargs)
print(f"Decorator: Creating instance with arguments: {args}, {kwargs}")
return instance
def __getattr__(self, name):
# Pass through attribute access to the original class
return getattr(self.original_class, name)
@MyDecorator
class BaseClass:
def __init__(self, arg1, arg2):
self.arg1 = arg1
self.arg2 = arg2
class SubClass(BaseClass):
def additional_method(self):
print("Additional method in SubClass")
# Creating an instance of the decorated base class
base_instance = BaseClass("foo", arg2="bar")
print(f"arg1: {base_instance.arg1}, arg2: {base_instance.arg2}")
# Creating an instance of the decorated subclass
sub_instance = SubClass("baz", arg2="qux")
print(f"arg1: {sub_instance.arg1}, arg2: {sub_instance.arg2}")
# Accessing additional method in the subclass
sub_instance.additional_method()
After discussion in the comment, I realize that a custom metaclass would be a simple solution. When you instantiate a class, you're actually calling the class' class' __call__
method which is the metaclass' __call__
.
You can do the interception there:
class MyMeta(type):
def __call__(self, *args, **kwargs):
print(f"Creating instance with arguments: {args}, {kwargs}")
return super().__call__(*args, **kwargs)
class BaseClass(metaclass=MyMeta):
def __init__(self, arg1, arg2):
self.arg1 = arg1
self.arg2 = arg2
class SubClass(BaseClass):
def additional_method(self):
print("Additional method in SubClass")
# Creating an instance of the decorated base class
base_instance = BaseClass("foo", arg2="bar")
print(f"arg1: {base_instance.arg1}, arg2: {base_instance.arg2}")
# Creating an instance of the decorated subclass
sub_instance = SubClass("baz", arg2="qux")
print(f"arg1: {sub_instance.arg1}, arg2: {sub_instance.arg2}")
# Accessing additional method in the subclass
sub_instance.additional_method()
output:
Creating instance with arguments: ('foo',), {'arg2': 'bar'}
arg1: foo, arg2: bar
Creating instance with arguments: ('baz',), {'arg2': 'qux'}
arg1: baz, arg2: qux
Additional method in SubClass
The point is that the metaclass applies to the whole inheritance chain.
It is now much better because the isinstance()
works normally. In your solution the type of the BaseClass
would become MyDecorator
which is wrong.