Разница в том, что вы мутировали в словарь .Первый простой пример с целыми числами работает с неизменяемыми целочисленными объектами.cls._X += 1
принимает значение _X
(из родительского класса, если необходимо), после чего операция old + 1
создает новый целочисленный объект, который затем присваивается обратно cls._X
.Назначение здесь имеет значение, так как это будет иметь место в дочернем классе.
Но вы ничего не присваивали классу со вторым регистром:
cls._callbacks[fmt] = {}
cls._callbacks[fmt]["loader"] = loader
Вы назначены наключ в словаре.Сам атрибут cls._callbacks
не изменяется, это один и тот же словарь, общий для всех классов.Ссылка cls._callbacks
ищется в базовом классе Image
, после чего сам словарь обновляется путем добавления пары ключ-значение.Ни один из подклассов (HSImage
или GT
) не имеет атрибута самостоятельно.
Вам потребуется создать копию и присвоить измененную копию:
cls._callbacks = {k: dict(v) for k, v in cls._callbacks.items()}
cls._callbacks[fmt] = {'loader': loader}
Это создаетскопируйте не только внешний словарь, но и все значения, потому что все они тоже словари, перед добавлением нового словаря для fmt
.Затем копия присваивается cls._callbacks
, эффективно создавая новый атрибут в подклассе, если его там еще не было.
Конечно, это не так эффективно;копия создается каждый раз при регистрации загрузчика .Было бы лучше создать новый словарный объект _callback
для каждого подкласса, но это становится утомительным и может быть легко забыто.Вместо этого вы можете автоматизировать , используя метод __init_subclass__
:
class Image:
def __init_subclass__(cls):
cls._callbacks = {}
@classmethod
def registerDataFormat(cls, fmt, loader):
if fmt in cls._callbacks:
print("The {} format has already been registered.".format(fmt))
return
cls._callbacks[fmt] = {'loader': loader}
Для каждого создаваемого подкласса вызывается метод __init_subclass__
.