Indulging in unary operators in python

    >>> +--+_+-+_++_+--_+_-_+-+-+-___++++_+-_-+++_+-+_--++--_
    'ПРИВЕТ, ХАБР!'

    What was it? Yes, you were not mistaken - this is Morse code with pluses instead of dots directly in Python syntax!

    If you do not understand how this works, or simply do not mind refreshing your knowledge on Soviet Army Day (and the Navy!), Welcome to cat.

    Unary operators in Python


    In Python, there are three unary operator: +, -and ~(bitwise negation). (There is still not, but this is a different story.) The interesting thing is that they can be combined in unlimited quantities:

    >>> ++-++-+---+--++-++-+1-1>>> -~-~-~-~-~-~-~-~-~-~111

    And all three of them can be redefined for their objects.

    But only two of them - plus and minus - have homonymous binary options. This will allow us to combine several sequences of pluses and minuses, each of which will be a single letter in Morse code, into a single valid expression: the line at the beginning is +--+_+-+_++_+--_+_-_+-+-+-___++++_+-_-+++_+-+_--++--_parsed as

    
    (+--+_) + (-+_) + (+_) + (--_) + _ - _ + (-+-+-___) + (+++_) + (-_) - (+++_) + (-+_) - (-++--_)
    

    It remains to determine the objects _(end of sequence) and ___(end of sequence and space).

    Overriding statements in Python


    To override operators in Python, you need to declare methods with special names in the class. So, for the unary plus and minus it __pos__, and __neg__, as for binary - it's just the four methods: __add__, __radd__, __sub__and __rsub__.

    Let's get a simple class whose instance will be ours _. First of all, he needs to support unary operators and accumulate facts of their application:

    classMorse(object):def__init__(self, buffer=""):
            self.buffer = buffer
        def__neg__(self):return Morse("-" + self.buffer)
        def__pos__(self):return Morse("." + self.buffer)

    Also, our object should be able to convert to a line. Let's start a dictionary with Morse code decoding and add a method __str__.

    Morse code
    morse_alphabet = {
        "А" : ".-",
        "Б" : "-...",
        "В" : ".--",
        "Г" : "--.",
        "Д" : "-..",
        "Е" : ".",
        "Ж" : "...-",
        "З" : "--..",
        "И" : "..",
        "Й" : ".---",
        "К" : "-.-",
        "Л" : ".-..",
        "М" : "--",
        "Н" : "-.",
        "О" : "---",
        "П" : ".--.",
        "Р" : ".-.",
        "С" : "...",
        "Т" : "-",
        "У" : "..-",
        "Ф" : "..-.",
        "Х" : "....",
        "Ц" : "-.-.",
        "Ч" : "---.",
        "Ш" : "----",
        "Щ" : "--.-",
        "Ъ" : "--.--",
        "Ы" : "-.--",
        "Ь" : "-..-",
        "Э" : "..-..",
        "Ю" : "..--",
        "Я" : ".-.-",
        "1" : ".----",
        "2" : "..---",
        "3" : "...--",
        "4" : "....-",
        "5" : ".....",
        "6" : "-....",
        "7" : "--...",
        "8" : "---..",
        "9" : "----.",
        "0" : "-----",
        "." : "......",
        "," : ".-.-.-",
        ":" : "---...",
        ";" : "-.-.-.",
        "(" : "-.--.-",
        ")" : "-.--.-",
        "'" : ".----.",
        "\"": ".-..-.",
        "-" : "-....-",
        "/" : "-..-.",
        "?" : "..--..",
        "!" : "--..--",
        "@" : ".--.-.",
        "=" : "-...-",
    }
    inverse_morse_alphabet = {v: k for k, v in morse_alphabet.items()}

    Method:

    def__str__(self):return inverse_morse_alphabet[self.buffer]
            # Если в словаре нет текущей последовательности,# то это KeyError. Ну и отлично.

    Next, binary addition and subtraction. They are left-associative in Python, that is, they will be executed from left to right. Let's start with a simple one:

    def__add__(self, other):return str(self) + str(+other)
            # Обратите внимание на унарный + перед other.

    So, after adding the first two sequences, we get a line. Will it be able to stack up with an object of the type following it Morse? No, addition with this type is str.__add__not provided. Therefore, Python will try to call a method on the right object __radd__. We realize it:

    def__radd__(self, s):return s + str(+self)

    It remains to do the same for subtraction:

    def__sub__(self, other):return str(self) + str(-other)
        def__rsub__(self, s):return s + str(-self)

    Whole class together
    classMorse(object):def__init__(self, buffer=""):
            self.buffer = buffer
        def__neg__(self):return Morse("-" + self.buffer)
        def__pos__(self):return Morse("." + self.buffer)
        def__str__(self):return inverse_morse_alphabet[self.buffer]
        def__add__(self, other):return str(self) + str(+other)
        def__radd__(self, s):return s + str(+self)
        def__sub__(self, other):return str(self) + str(-other)
        def__rsub__(self, s):return s + str(-self)


    Let's write a simple function that will convert us strings into Python code:

    defmorsify(s):
        s = "_".join(map(morse_alphabet.get, s.upper()))
        s = s.replace(".", "+") + ("_"if s else"")
        return s

    Now we can hammer all this beauty into the console and see that the code works:
    >>> morsify("ПРИВЕТ,ХАБР!")
    '+--+_+-+_++_+--_+_-_+-+-+-_++++_+-_-+++_+-+_--++--_'>>> _ = Morse()
    >>> +--+_+-+_++_+--_+_-_+-+-+-_++++_+-_-+++_+-+_--++--_
    'ПРИВЕТ,ХАБР!'


    Add space support


    Let's make an object that behaves like Morsejust add a space at the end.

    classMorseWithSpace(Morse):def__str__(self):return super().__str__() + " "
    ___ = MorseWithSpace()

    Simply? Yes! Works? No :-(

    So that in the process, objects of type are MorseWithSpacenot replaced by objects of type Morse, you must also change __pos__and __neg__:

    def__neg__(self):return MorseWithSpace(super().__neg__().buffer)
        def__pos__(self):return MorseWithSpace(super().__pos__().buffer)

    It is also worth adding an entry " " : " "to the Morse code dictionary and changing the morsify function a little bit:

    defmorsify(s):
        s = "_".join(map(morse_alphabet.get, s.upper()))
        s = s.replace(".", "+") + ("_"if s else"")
        s = s.replace("_ ", "__").replace(" _", "__")
        return s

    Works!

    >>> morsify("ПРИВЕТ, ХАБР!")
    '+--+_+-+_++_+--_+_-_+-+-+-___++++_+-_-+++_+-+_--++--_'>>> ___ = MorseWithSpace()
    >>> +--+_+-+_++_+--_+_-_+-+-+-___++++_+-_-+++_+-+_--++--_
    'ПРИВЕТ, ХАБР!'

    All code in Gist .

    Conclusion


    Overriding operators can take you far and long.

    Do not abuse it!

    Also popular now: