Unary operators are operators which work only on the object itself, so not in combination with another object. A typical example is using the minus (-) sign in front of a number to turn it into a negative number. You can overload some of the unary operators, and also some of the basic functions that work on an object.

class Quaternion:
    def __init__( self, a, b, c, d ):
        self.a, self.b, self.c, self.d = a, b, c, d
    def __repr__( self ):
        return "({},{}i,{}j,{}k)".format( self.a, self.b,
            self.c, self.d )
    def __neg__( self ):
        return Quaternion( -self.a, -self.b, -self.c, -self.d)
    def __abs__( self ):
        return Quaternion( abs( self.a ), abs( self.b ),
            abs( self.c ), abs( self.d ) )
    def __bytes__( self ):
        return self.__str__().encode( "utf-8" )

c1 = Quaternion( 3, -4, 5, -6 )
print( c1 )
print( -c1 )
print( abs( c1 ) )
print( bytes( c1 ) )

Note

You might think it would be a good idea to also implement the __int__(), __float__(), and __round__() methods, that, respectively, use the int(), float(), and round() functions on self.a, self.b, self.c, and self.d. Unfortunately, that cannot be done, as these methods must return integers or floats, and not Quaternions. Other than what I propose, I see no sensible interpretation of int(), float(), and round() for Quaternion, so these methods should not be implemented.