How to emulate 4-bit integer in Python 3?

前端 未结 3 1865
挽巷
挽巷 2021-01-24 09:24

I\'d like to emulate overflow behavior of unsigned 4-bit integers, like this:

>>> x, y = Int4(10), Int4(9)
>>> x + y
Int4(3)
>>> x * y         


        
相关标签:
3条回答
  • 2021-01-24 10:14

    No, subclassing int will not automatically re-use the type when applying arithmetic to it:

    >>> class Int4(int):
    ...     def __new__(cls, i):
    ...         return super(Int4, cls).__new__(cls, i & 0xf)
    ... 
    >>> x, y = Int4(10), Int4(9)
    >>> x + y
    19
    >>> type(x + y)
    <type 'int'>
    

    You have to override the __add__, etc. methods to cast back to Int4() when you do this.

    If you only ever want to support the type itself (e.g. not support converting other numeric types in the process), then you could can generate most of these:

    from functools import wraps
    
    class Int4(int):
        def __new__(cls, i):
            return super(Int4, cls).__new__(cls, i & 0xf)
    
    def add_special_method(cls, name):
        mname = '__{}__'.format(name)
        @wraps(getattr(cls, mname))
        def convert_to_cls(self, other):
            bound_original = getattr(super(cls, self), mname)
            return type(self)(bound_original(other))
        setattr(cls, mname, convert_to_cls)
    
    for m in ('add', 'sub', 'mul', 'floordiv', 'mod', 'pow',
              'lshift', 'rshift', 'and', 'xor', 'or'):
        add_special_method(Int4, m)
        add_special_method(Int4, 'r' + m)  # reverse operation
    

    This produces methods that always return the type of self from arithmetic special methods; this'll allow for further subclassing of Int4 as well.

    Demo:

    >>> from functools import wraps
    >>> class Int4(int):
    ...     def __new__(cls, i):
    ...         return super(Int4, cls).__new__(cls, i & 0xf)
    ... 
    >>> def add_special_method(cls, name):
    ...     mname = '__{}__'.format(name)
    ...     @wraps(getattr(cls, mname))
    ...     def convert_to_cls(self, other):
    ...         bound_original = getattr(super(cls, self), mname)
    ...         return type(self)(bound_original(other))
    ...     setattr(cls, mname, convert_to_cls)
    ... 
    >>> for m in ('add', 'sub', 'mul', 'floordiv', 'mod', 'pow',
    ...           'lshift', 'rshift', 'and', 'xor', 'or'):
    ...     add_special_method(Int4, m)
    ...     add_special_method(Int4, 'r' + m)  # reverse operation
    ... 
    >>> x, y = Int4(10), Int4(9)
    >>> x + y
    3
    >>> x * y
    10
    
    0 讨论(0)
  • 2021-01-24 10:19

    This isn't as clever as @martijn-pieters' answer, but it does seem to work on python 2.7 and 3.*, whereas I get AttributeError: 'wrapper_descriptor' object has no attribute '__module__' on python 2.7 with that answer.

    import sys
    
    lt_py3 = sys.version_info < (3,)
    lt_py33 = sys.version_info < (3, 3)
    
    
    class Int(int):
        '''
        int types
        '''
        def __new__(self, val=0):
            return int.__new__(self, val & (1 << self.bits - 1) - 1)
    
        def __max_type_bits(self, other):
            '''
            determine the largest type and bits available from those in `self` and
            `other`
            '''
            if hasattr(other, 'bits'):
                if self.bits < other.bits:
                    return type(other), other.bits
            return type(self), self.bits
    
        def __unary_typed(oper):
            '''
            return a function that redefines the operation `oper` such that the
            result conforms to the type of `self`
            '''
            def operate(self):
                return type(self)(oper(self))
            return operate
    
        def __typed(oper):
            '''
            return a function that redefines the operation `oper` such that the
            result conforms to the type of `self` or `other`, whichever is larger
            if both are strongly typed (have a `bits` attribute); otherwise return
            the result conforming to the type of `self`
            '''
            def operate(self, other):
                typ, bits = self.__max_type_bits(other)
                return typ(oper(self, other))
            return operate
    
        def __unary_ranged(oper):
            '''
            return a function that redefines the operator `oper` such that the
            result conforms to both the range and the type of `self`
            '''
            def operate(self, other):
                '''
                type and bitmask the result to `self`
                '''
                return type(self)(oper(self) & (1 << self.bits - 1) - 1)
            return operate
    
        def __ranged(oper):
            '''
            return a function that redefines the operator `oper` such that the
            result conforms to both the range and the type of `self` or `other`,
            whichever is larger if both are strongly typed (have a `bits`
            attribute); otherwise return the result conforming to the type of
            `self`
            '''
            def operate(self, other):
                '''
                type and bitmask the result to either `self` or `other` whichever
                is larger
                '''
                typ, bits = self.__max_type_bits(other)
                return typ(oper(self, other) & (1 << bits - 1) - 1)
            return operate
    
        # bitwise operations
        __lshift__  = __ranged(int.__lshift__)
        __rlshift__ = __ranged(int.__rlshift__)
        __rshift__  = __ranged(int.__rshift__)
        __rrshift__ = __ranged(int.__rrshift__)
        __and__     = __typed(int.__and__)
        __rand__    = __typed(int.__rand__)
        __or__      = __typed(int.__or__)
        __ror__     = __typed(int.__ror__)
        __xor__     = __typed(int.__xor__)
        __rxor__    = __typed(int.__rxor__)
        __invert__  = __unary_typed(int.__invert__)
    
        # arithmetic operations
        if not lt_py3:
            __ceil__  = __unary_typed(int.__ceil__)
            __floor__ = __unary_typed(int.__floor__)
            __int__   = __unary_typed(int.__int__)
        __abs__       = __unary_typed(int.__abs__)
        __pos__       = __unary_typed(int.__pos__)
        __neg__       = __unary_ranged(int.__neg__)
        __add__       = __ranged(int.__add__)
        __radd__      = __ranged(int.__radd__)
        __sub__       = __ranged(int.__sub__)
        __rsub__      = __ranged(int.__rsub__)
        __mod__       = __ranged(int.__mod__)
        __rmod__      = __ranged(int.__rmod__)
        __mul__       = __ranged(int.__mul__)
        __rmul__      = __ranged(int.__rmul__)
        if lt_py3:
            __div__   = __ranged(int.__div__)
            __rdiv__  = __ranged(int.__rdiv__)
        __floordiv__  = __ranged(int.__floordiv__)
        __rfloordiv__ = __ranged(int.__rfloordiv__)
        __pow__       = __ranged(int.__pow__)
        __rpow__      = __ranged(int.__rpow__)
    
    
    class Int4(Int):
        bits = 4
    
    x, y = Int4(10), Int4(9)
    print(x + y)
    print(x*y)
    

    Running this code in a file called answer.py produces

    $ python2.7 answer.py 
    3
    2
    $ python3.4 answer.py 
    3
    2
    
    0 讨论(0)
  • 2021-01-24 10:30

    Overriding the __add__ method is a good idea, because you can make your calculations look clearly. Int4(4) + Int4(7) looks better than Int4(4).addTo(Int4(7)) (or something like this). Here is the code that could help you:

    class Int4:
      def __init__(self, num): # initialising
        self.num = self.cap(num)
    
      def __str__(self):
        return str(self.num)
    
      def __repr__(self):
        return "Int4(" + self.__str__() + ")"
    
      def __add__(self, other): # addition
        return Int4(self.cap(self.num + other.num))
    
      def __sub__(self, other): # subtraction
        return Int4(self.cap(self.num - other.num))
    
      @staticmethod
      def cap(num): # a method that handles an overflow
        while num < 0:
          num += 16
        while num >= 16:
          num -= 16
        return num
    

    And testing it:

    >>> x,y,z = Int4(5), Int4(8), Int4(12)
    >>> x
    Int4(5)
    >>> y
    Int4(8)
    >>> z
    Int4(12)
    >>> print x+y
    13
    >>> print z+y
    4
    >>> print x-z
    9
    
    0 讨论(0)
提交回复
热议问题