I am trying to make a class Sprite
that inherits generic class T
, with the bound of being a subclass of class Object
. Class Text
is a subclass of class Object
. This is class Text
, provided by outer library:
# text.py
class Text(Object):
# ignored properties...
def __init__(self, text="placeholder text", **kwargs):
super().__init__(object_type=Text.object_type, text=text, **kwargs)
This is my self-written class Sprite
:
# sprite.py
from typing import TypeVar, Generic
T = TypeVar('T', bound=Object)
class Sprite(Generic[T]):
def __init__(self, **kwargs):
super(Sprite, self).__init__(self, clickable=True, evt_handler=self.click_wrapper, **kwargs)
And such a Sprite
instance is initialized by:
sprite = Sprite[Text](
text="This is a sprite!",
object_id="spriteTest",
# other similar arguments...
)
And this is the error thrown:
Exception thrown in main()! Terminating main...
Traceback (most recent call last):
File "sprite.py", line 79, in main
sprite = Sprite[Text](
File "C:\ProgramData\Anaconda3\lib\typing.py", line 687, in __call__
result = self.__origin__(*args, **kwargs)
File "sprite.py", line 47, in __init__
super(Sprite, self).__init__(self, clickable=True, evt_handler=self.click_wrapper, **kwargs)
TypeError: object.__init__() takes exactly one argument (the instance to initialize)
Why is this not working?
CodePudding user response:
I believe you are misunderstanding Generic Type variables here. Let me first try to reduce your bug down to its most minimal variant:
class Sprite:
def __init__(self, **kwargs):
super().__init__(**kwargs)
Sprite(text="My text")
This very simple program throws the exact same Exception as you have:
Traceback (most recent call last):
File ".../72690805.py", line 57, in <module>
Sprite(text="My text")
File ".../72690805.py", line 55, in __init__
super().__init__(**kwargs)
TypeError: object.__init__() takes exactly one argument (the instance to initialize)
The key here is that it is object
for who you cannot specify anything other than one argument. With other words, the superclass of Sprite
is object
in both of our cases (i.e. the default Python object, not your Object
class). Your Sprite
class simply does not have a non-default superclass.
You seem to be of the understanding that your super(...).__init__(...)
call will initialize Text
whenever you use Sprite[Text](...)
, but that is not the case. Let me give a common example of a Generic type variable in use:
from typing import List, TypeVar, Generic
T = TypeVar('T')
class Queue(Generic[T]):
def __init__(self) -> None:
super().__init__()
self.queue: List[T] = []
def put(self, task: T) -> None:
self.queue.append(task)
def get(self) -> T:
return self.queue.pop(-1)
queue = Queue()
queue.put(12)
queue.put(24)
queue.put(36)
# queue.put('a') # <- A type checker should disallow this
print(queue.get())
print(queue.get())
print(queue.get())
Here, we have a simple Queue class, with put
and get
methods. These functions are supplemented with Type hints via T, and now type checkers know that e.g. Queue[int]().get
returns an int
.
However, the super().__init__()
is still just the standard initialization of the Python object
. We're not suddenly initializing an int
, which is equivalent to what you seem to be trying.
To wrap up; whenever you find yourself using functionality from the typing
module to try and get something working, then you're making a mistake. As far as I'm aware, all the functionality from typing
is merely "cosmetic", and is ignored by Python. It exists to allow developers to use type checkers to ensure that they are not making mistakes, e.g. calling queue.put('a')
when queue
was initialized with Queue[int]()
. To reiterate, this put
of a character will still execute in Python, and it will place the character in the queue, even though a Type checker would tell you that it's wrong.