Tag “Hacks”

In my opinion, there are three levels of learning a language. First of all, you learn basic grammar and vocabulary. Then you learn specific things such as idioms and advanced constructions. And finally, you learn obscene language. It is very personal, where and when to use the latter. But we cannot deny the fact that swearing makes speech more expressive.

The swearwords in programming languages are called “dirty hacks”. Usually, it is strongly recommended to avoid them. However, hacking sometimes makes program better. Let’s take a look at some obscene Python.

>>> class A: pass
...
>>> class B: pass
...
>>> a = A()
>>> isinstance(a, A)
True
>>> a.__class__ = B
>>> isinstance(a, A)
False
>>> isinstance(a, B)
True

Well, you would think: “If someone from my team used this feature, I would commit a murder.” Frankly, it is not a feature. It is hard to believe, that Guido van Rossum and other Python developers were thinking about it: “We definitely need an ability to change object’s class in runtime.” It is rather a side effect of Python design. Anyway, I’m going to change your mind about this hack.

Imagine a CSM, where each page is described by regular Python dictionary object (it is stored in MongoDB, for example). So, you need a way to map these objects to some more useful ones. Obviously, each page has at least title and body:

class Page(object):
    """ A base class for representing pages """

    def __init__(self, data):
        self.title = data['title']
        self.body = data['body']

Also, page may have a number of additional widgets, which can be represented by mixins:

class Commentable(object):
    """ Adds comments on Page """

    def get_comments(self, page_num=1):
        """ Get list of comments for specified page number """

    def add_comment(self, user, comment):
        """ User comments Page """

    def remove_comment(self, comment_id):
        """ Moderator or comment author removes comment from Page """


class Likeable(object):
    """ Adds "like/dislike" buttons on Page """

    def like(self, user):
        """ User likes Page """

    def dislike(self, user):
        """ User dislikes Page """


class Favoritable(object):
    """ Adds "favorite" button on Page """

    def add_to_favorites(self, user):
        """ User adds Page to favorites """

    def remove_from_favorites(self, user):
        """ User removes Page from favorites """

The problem is how to get them together. A classical solution from “Design Patterns” by Gang of Four is a factory. It may be an additional class or function which takes a page descriptor dictionary, extracts mixin set, builds class based on Page and specified mixins, and returns an object of this class. But why do we need this additional entity? Let’s do it inside Page class directly:

class Page(object):
    """ A base class for representing pages """

    mixins = {}     # a map of registered mixins
    classes = {}    # a map of classes for each mixin combination

    @classmethod
    def mixin(cls, class_):
        """ Decorator registers mixin class """
        cls.mixins[class_.__name__] = class_
        return class_

    @classmethod
    def get_class(cls, mixin_set):
        """ Returns class for given mixin combination """
        mixin_set = tuple(mixin_set)    # Turn list into hashable type
        if mixin_set not in cls.classes:
            # Build new class, if it doesn't exist
            bases = [cls.mixins[class_name] for class_name in mixin_set]
            bases.append(Page)
            name = ''.join(class_.__name__ for class_ in bases)
            cls.classes[mixin_set] = type(name, tuple(bases), {})
        return cls.classes[mixin_set]

    def __init__(self, data):
        self.title = data['title']
        self.body = data['body']
        self.__class__ = self.get_class(data['mixins'])    # Fu^WHack you!!!

...register our mixins:

@Page.mixin
class Commentable(object):
    """ Adds comments on Page """


@Page.mixin
class Likeable(object):
    """ Adds "like/dislike" buttons on Page """


@Page.mixin
class Favoritable(object):
    """ Adds "favorite" button on Page """

...and test it:

somepage = Page({
    'title': 'Lorem Ipsum',
    'body': 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
    'mixins': ['Commentable', 'Likeable'],
})

assert somepage.__class__.__name__ == 'CommentableLikeablePage'
assert isinstance(somepage, Commentable)
assert isinstance(somepage, Likeable)
assert isinstance(somepage, Page)

See full source of example.

So what did we get? We got a beautiful solution based on a questionable feature. This is exactly the same situation when using bad words makes speech better. Do you agree? No? Hack you!

P.S. If you combine this with Pyramid traversal, you will get a super flexible and powerful CMS. But this is another story.