According to this answer, a class object cls
can be replicated with
cls_copy = type('cls_copy', cls.__bases__, dict(cls.__dict__))
This works perfectly for most normal cases. It does not work when the metaclass of cls
is not type
. My initial naive fix was to do
cls_copy = type(cls)('cls_copy', cls.__bases__, dict(cls.__dict__))
However, this is simply pointless. There is no way to know what a metaclass does, as this answer to a related question points out, how it transforms the input dictionary, what additional keywords it requires, etc.
The original use of type
is almost good enough with a couple of minor exceptions:
- The
__dict__
created by metaclasses that do not end up callingtype.__new__
may be of a different type than the usual proxy object. - Classes extending the copy will not have the correct metaclass, which may cause unexpected behavior.
- Any properties or other data descriptors defined in the original metaclass will no longer be available on the class object.
I am willing to ignore item #1. It is a corner case that I am willing to document away should I find a viable solution to the other items. Items #2 and #3 can be solved if it were possible to change the metaclass of the copy. I tried (again, naively)
cls_copy = type('cls_copy', cls.__bases__, dict(cls.__dict__),
metaclass=type(cls))
This just raised a TypeError
, as could be expected:
TypeError: __init_subclass__() takes no keyword arguments
This makes sense in light of the docs:
Like its identity, an object’s type is also unchangeable. 1
However, the footnote states that
It is possible in some cases to change an object’s type, under certain controlled conditions. It generally isn’t a good idea though, since it can lead to some very strange behaviour if it is handled incorrectly.
What are the conditions under which it is possible to change an object's type, specifically that of a class? Is this one of those cases, and if so, how?
Note
I am aware that copy.deepcopy
and inheritance are viable alternatives here. For the purpose of this question, I wish to ignore those alternatives and stick with using type
-related techniques.
You could use
type.__new__(type(cls), cls.__name__, cls.__bases__, dict(cls.__dict__))
. This uses the normaltype
creation process, but creates an instance oftype(cls)
instead oftype
.As for the
__metaclass__
issue, I think that is because__metaclass__
is usually what is called, sotype
can't use it.