You cannot refer to MetaDataElement while it is being constructed, since it does not yet exist.  Thus,
class MetaDataElement:
    (MD_INVALID, MD_CATEGORY, MD_TAG) = range(3)
    mapInitiator2Type = {'!':MetaDataElement.MD_CATEGORY, 
                         '#':MetaDataElement.MD_TAG}
fails because the very construction of mapInitiator2Type requires MetaDataElement to have attributes, which it does not yet have.  You can think of your constants MD_INVALID, etc. as variables that are local to the construction of your class.  This is why the following works, as icktoofay wrote:
class MetaDataElement:
    (MD_INVALID, MD_CATEGORY, MD_TAG) = range(3)
    mapInitiator2Type = {'!': MD_CATEGORY,  # MD_CATEGORY is like a local variable!
                         '#': MD_TAG}
However, you can refer to the class MetaDataElement in any yet un-interpreted piece of code, as in
    def method_of_MetaDataElement():
        print MetaDataElement.MD_TAG
You even have to refer to MetaDataElement, here, because MD_TAG is not a kind of local variable when method_of_MetaDataElement() is executed (MD_TAG was only defined as a kind of local variable during class construction).  Once the class MetaDataElement is created, MD_TAG is simply a class attribute, which is why method_of_MetaDataElement() must refer to it as such.