There are many algorithms out there that uses some “magic constants”. An example is the S-box in most of the block ciphers, which we cannot possibly avoid from the implementation. In some languages, we can hide them in a namespace. For instance in JavaScript, we can put everything inside a pair of braces, which not only confine the scope of the identifiers defined, but also presenting their logical groupping. Python, however, do not have such option unless we put it in a separate module.
One alternative in Python is class. We can put all such nuts and blots in a
class. But doing so means we have a different syntax, like this:
classobj = MyClass()
classobj.function(x)
Can I just call the class method without creating a class object? It turns out
not so intuitive. A python class has a special method __call__ that can be
overridden such that a class object can be invoked just like a function:
class CallMe:
delimiter = "$"
def __call__(self, message):
print(self.delimiter + str(message) + self.delimiter)
x = CallMe()
x("foo") # print $foo$
This is useful if the class is a factory that produces a callable that needs
to be initialized. If you have nothing to initialize, that line x = CallMe()
seems to be redundant. We cannot expect to do CallMe("foo") instead because
that looks really like invoking the constructor – and a constructor means an
object is created.
This is how metaclass arises. If we want to modify the language behavior of a
class, we have to do that in its metaclass. In this case, we want to override
the constructor such that CallMe("foo") does not mean to create an object.
This is what to do:
class Meta(type):
def __call__(cls, message):
print(cls.delimiter + str(message) + cls.delimiter)
class CallMe(metaclass=Meta):
delimiter = "$"
CallMe("foo") # print $foo$
Before Python 3, the way to specify metaclass is to define class variable
__metaclass__ = Meta inside the class CallMe. This usage is removed and we
have to specify as above. By specifing the __call__ function in the
metaclass, the behavior of CallMe("foo") is modified. And as the __call__
function does not return an object of class cls, the class CallMe will not
be incarnate. Note that the first parameter of __call__ is cls to signal
that functions in the metaclass is always dealing with the class type, not the
class object.
Indeed, we can avoid the metaclass:
class CallMe:
delimiter = "$"
def __new__(self, message):
print(self.delimiter + str(message) + self.delimiter)
CallMe("foo") # print $foo$
This is another way to override the constructor in Python. Behavior will be the
same as the approach using metaclass. Actually for the expression
CallMe("foo"), the order of resolution will be the __call__ of metaclass
take the highest precedence, then __new__ of the calss, then __init__ of
the class as a fallback.