I have a very weird issue with dict
inheritance. I want to be able to pass custom objects in an instance of the dict
object, so I made a custom __new__
method to format these objects so they are compatible with the dict.__new__
method.
But I am facing a very strange issue. I already read most of the topics talking about this error one stackoverflow, but none are going as far as my code is. Here is my code:
class TextureItem:
def __init__(self, name: str = "stone") -> None:
self.name = name
def __repr__(self) -> str:
return f'TextureItem({self.name})'
class GlobalPalette(dict[str, TextureItem]):
def __new__(cls, *args, **kwargs):
new_kwargs = {}
print("args and kwargs:", args, kwargs, "\n")
for arg in args:
if isinstance(arg, list):
for item in arg:
new_kwargs[item.name] = item
elif isinstance(arg, dict):
new_kwargs.update(arg)
elif isinstance(arg, TextureItem):
new_kwargs[arg.name] = arg
if kwargs:
new_kwargs.update(kwargs)
print("new_kwargs:", new_kwargs, "\n")
return super().__new__(cls, **new_kwargs)
a = GlobalPalette(stone=TextureItem(), v="OK")
print("a:", a, "\n\n")
b = GlobalPalette([TextureItem()], v="OK")
print("b:", b)
This is what I get when running the code:
args and kwargs: () {'stone': TextureItem(stone), 'v': 'OK'}
new_kwargs: {'stone': TextureItem(stone), 'v': 'OK'}
a: {'stone': TextureItem(stone), 'v': 'OK'}
args and kwargs: ([TextureItem(stone)],) {'v': 'OK'}
new_kwargs: {'stone': TextureItem(stone), 'v': 'OK'}
Traceback (most recent call last):
File "/Users/n/ainbt/t.py", line 28, in <module>
b = GlobalPalette([TextureItem()], v="OK")
TypeError: cannot convert dictionary update sequence element #0 to a sequence
The error at the end means I filled an iterable like a list
, tuple
or set
, that contains only "single" objects. Example: [("a": 1), ("b": 2)]
will be ok, but ["a", "b"]
will raise this error.
This means it comes from the list [TextureItem()]
in b = GlobalPalette([TextureItem()], v="OK")
. But why is it detecting it, my own __new__
should be executed before the super().__new__
.
Where am I going wrong ? Thanks
You cannot use __new__
to set dictionary items; that can only be done in the __init__
method. That is, consider the following example:
>>> dict.__new__(dict, v="OK")
{}
That is exactly what your code is doing.
I think you want to override __init__
, not __new__
, like this:
class TextureItem:
def __init__(self, name: str = "stone") -> None:
self.name = name
def __repr__(self) -> str:
return f'TextureItem({self.name})'
class GlobalPalette(dict[str, TextureItem]):
def __new__(cls, *args, **kwargs):
new_kwargs = {}
print("args and kwargs:", args, kwargs, "\n")
for arg in args:
if isinstance(arg, list):
for item in arg:
new_kwargs[item.name] = item
elif isinstance(arg, dict):
new_kwargs.update(arg)
elif isinstance(arg, TextureItem):
new_kwargs[arg.name] = arg
if kwargs:
new_kwargs.update(kwargs)
print("new_kwargs:", new_kwargs, "\n")
return super().__new__(cls, **new_kwargs)
a = GlobalPalette(stone=TextureItem(), v="OK")
print("a:", a, "\n\n")
b = GlobalPalette([TextureItem()], v="OK")
print("b:", b)
Running this results in:
args and kwargs: () {'stone': TextureItem(stone), 'v': 'OK'}
new_kwargs: {'stone': TextureItem(stone), 'v': 'OK'}
a: {'stone': TextureItem(stone), 'v': 'OK'}
args and kwargs: ([TextureItem(stone)],) {'v': 'OK'}
new_kwargs: {'stone': TextureItem(stone), 'v': 'OK'}
b: {'stone': TextureItem(stone), 'v': 'OK'}