📜 ⬆️ ⬇️

We indulge in unary operators in Python

>>> +--+_+-+_++_+--_+_-_+-+-+-___++++_+-_-+++_+-+_--++--_ ', !' 

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

If you don’t understand how it works, or just don’t mind refreshing your knowledge on the Day of the Soviet Army (and the Navy!), Welcome to Cat.

Unary operators in Python


There are three unary operators in Python: + , - and ~ (bitwise negation). (There is still not , but this is a separate story.) It is interesting that they can be combined in unlimited quantities:

 >>> ++-++-+---+--++-++-+1 -1 >>> -~-~-~-~-~-~-~-~-~-~1 11 

And all three of them can be redefined for their objects.
')
But only two of them - plus and minus - have homonymous binary options. This is what will allow us to combine several sequences of pros and cons, each of which will be one letter in Morse code, into a single valid expression: the line given at the beginning +--+_+-+_++_+--_+_-_+-+-+-___++++_+-_-+++_+-+_--++--_ as

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

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

Redefining Operators in Python


To redefine operators in Python, you need to declare methods in the class with special names. So, for unary plus and minus, these are __pos__ and __neg__ , and for binary ones, these are four methods at once: __add__ , __radd__ , __sub__ and __rsub__ .

Let's get a simple class whose instance is our _ . First of all, it needs to support unary operators and accumulate the facts of their application:

 class Morse(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 create a dictionary with Morse code decoding and add the __str__ method.

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.   . 

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

  def __add__(self, other): return str(self) + str(+other) #     +  other. 

So, after adding the first two sequences, we get a string. Can she stack up with an object like Morse next to her? No, addition with this type in str.__add__ not provided. Therefore, Python will attempt to call the __radd__ method on the right object. We implement it:

  def __radd__(self, s): return s + str(+self) 

It remains to do likewise for subtraction:

  def __sub__(self, other): return str(self) + str(-other) def __rsub__(self, s): return s + str(-self) 

Whole class together
 class Morse(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 to Python code:

 def morsify(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 support for spaces


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

 class MorseWithSpace(Morse): def __str__(self): return super().__str__() + " " ___ = MorseWithSpace() 

Simply? Yes! Works? Not :-(

In order to ensure that objects of type MorseWithSpace not replaced by objects of type Morse during work, you must also change __pos__ and __neg__ :

  def __neg__(self): return MorseWithSpace(super().__neg__().buffer) def __pos__(self): return MorseWithSpace(super().__pos__().buffer) 

You should also add the entry " " : " " to the Morse code dictionary and change a bit the morsify function:

 def morsify(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


Redefining operators can get you far and long.

Do not abuse them!

Source: https://habr.com/ru/post/349776/


All Articles