Reclassing an instance in Python Reclassing an instance in Python python python

Reclassing an instance in Python


Reclassing instances like this is done in Mercurial (a distributed revision control system) when extensions (plugins) want to change the object that represent the local repository. The object is called repo and is initially a localrepo instance. It is passed to each extension in turn and, when needed, extensions will define a new class which is a subclass of repo.__class__ and change the class of repo to this new subclass!

It looks like this in code:

def reposetup(ui, repo):    # ...    class bookmark_repo(repo.__class__):         def rollback(self):            if os.path.exists(self.join('undo.bookmarks')):                util.rename(self.join('undo.bookmarks'), self.join('bookmarks'))            return super(bookmark_repo, self).rollback()         # ...    repo.__class__ = bookmark_repo 

The extension (I took the code from the bookmarks extension) defines a module level function called reposetup. Mercurial will call this when initializing the extension and pass a ui (user interface) and repo (repository) argument.

The function then defines a subclass of whatever class repo happens to be. It would not suffice to simply subclass localrepo since extensions need to be able to extend each other. So if the first extension changes repo.__class__ to foo_repo, the next extension should change repo.__class__ to a subclass of foo_repo and not just a subclass of localrepo. Finally the function changes the instanceø's class, just like you did in your code.

I hope this code can show a legitimate use of this language feature. I think it's the only place where I've seen it used in the wild.


I'm not sure that the use of inheritance is best in this case (at least with regards to "reclassing"). It seems like you're on the right track, but it sounds like composition or aggregation would be best for this. Here's an example of what I'm thinking of (in untested, pseudo-esque code):

from copy import copy# As long as none of these attributes are defined in the base class,# this should be safeclass SkilledProgrammer(Programmer):    def __init__(self, *skillsets):        super(SkilledProgrammer, self).__init__()        self.skillsets = set(skillsets)def teach(programmer, other_programmer):    """If other_programmer has skillsets, append this programmer's       skillsets.  Otherwise, create a new skillset that is a copy       of this programmer's"""    if hasattr(other_programmer, skillsets) and other_programmer.skillsets:        other_programmer.skillsets.union(programmer.skillsets)    else:        other_programmer.skillsets = copy(programmer.skillsets)def has_skill(programmer, skill):    for skillset in programmer.skillsets:        if skill in skillset.skills            return True    return Falsedef has_skillset(programmer, skillset):    return skillset in programmer.skillsetsclass SkillSet(object):    def __init__(self, *skills):        self.skills = set(skills)C = SkillSet("malloc","free","pointer arithmetic","curly braces")SQL = SkillSet("SELECT", "INSERT", "DELETE", "UPDATE")Bob = SkilledProgrammer(C)Jill = Programmer()teach(Bob, Jill)          #teaches Jill Chas_skill(Jill, "malloc") #should return Truehas_skillset(Jill, SQL)   #should return False

You may have to read more about sets and arbitrary argument lists if you aren't familiar with them to get this example.


This is fine. I've used this idiom plenty of times. One thing to keep in mind though is that this idea doesn't play well with old-style classes and various C extensions. Normally this wouldn't be an issue, but since you are using an external library you'll just have to make sure you're not dealing with any old-style classes or C extensions.