1

With my limited understanding of @property,@setter, and @getter, I came up with following code.

class BitCounts:
    sign_bit = 0
    exponent_bits = 0
    mantissa_bits = 0
    _total_bits = 0

    @property
    def total_bits(self):
        return self._total_bits

    @total_bits.setter
    def total_bits(self):
        self._total_bits = self.sign_bit + self.exponent_bits + self.mantissa_bits


class Single(BitCounts):
    sign_bit = 1
    offset = 0x7F
    exponent_bits = 8
    mantissa_bits = 23
    _total_bits = BitCounts.total_bits


class Double(BitCounts):
    sign_bit = 1
    offset = 0x400
    exponent_bits = 11
    mantissa_bits = 52
    _total_bits = BitCounts.total_bits

My intention is to use the subclass Single and Double in other functions as a set of options like so, for example:

    def some_function(option=Single):
        print("exponent bit counts are: %d", option.exponent_bits)
        print("mantissa bit counts are: %d", option.mantissa_bits)
        print("total bit counts are: %d", option.total_bits)

I would like total_bits to be automatically recalculated using values from subclass Single or Double.

I am trying to avoid extra functions to perform the calculation at subclass level.

With above codes, by calling Single.total_bits, or Double.total_bits, I am only getting a message saying <property object at 0x000002258DF7CB30>, what did I do wrong, and how can I fix it?

user97662
  • 942
  • 1
  • 10
  • 29

3 Answers3

2

The way you are using subclasses with hard-coded static values suggests these should be instances not subclasses. This is also suggested by your temptation to use self even though you haven't made any instances. self refers to a particular instance.

Also, setters typically take a value as an argument. You don't have that in your setter because total_bits is completely dependent on other values. As such you should just move your setter calculation to the getter and return the result of the calculation.

Consider:

class BitCounts:
    def __init__(self, sign,offset, exponent, mantissa):       
        self.sign_bit = sign
        self.offset = offset
        self.exponent_bits = exponent
        self.mantissa_bits = mantissa

    @property
    def total_bits(self):
        return self.sign_bit + self.exponent_bits + self.mantissa_bits

# now make two instances:
single = BitCounts(1, 0x7F, 8, 23 )
double = BitCounts(1, 0x400, 11, 52)

print(single.total_bits)
# 32

print(double.total_bits)
# 64
Mark
  • 90,562
  • 7
  • 108
  • 148
  • I like this solution a lot, however, I noticed that by using `@property`, each time `single.total_bits` is called, it will perform recalculation. Is there a way to avoid that because it can get expensive with complicated calculations with multiple calls to `single or double.total_bits`. – user97662 Nov 28 '21 at 01:53
  • @user97662 you could cache it, but you would need to update the cache when any of the dependent variables (like `sign_bit`) changed. That's only a problem if those values might change. If those values never change, you can set the value once as a regular instance variable inside `__init__`. with `self.total_bits = self.sign_bit + self.exponent_bits + self.mantissa_bits`. Then you don't need a getter at all. Careful of premature optimization thought. Addition is cheap. – Mark Nov 28 '21 at 01:59
1

You can use:

class Single(BitCounts):
    sign_bit = 1
    offset = 0x7F
    exponent_bits = 8
    mantissa_bits = 23
    _total_bits = BitCounts.total_bits

    def get_total_bits(self):
        # Update values here, example below
        self._total_bits = self._total_bits + 1
        return self._total_bits

Then call:

option = Single()
option.get_total_bits()
1

The problem here is that you are trying to call an instance method from a class.

class A:
  @property
  def foo(self):
    return 1

print(A.foo) # prints an object of function "foo"
print(A().foo) # prints "1"

To accomplish this, at least from my knowledge you need to use a metaclass similar to what they do here: Per-class @property decorator in Python

bguest
  • 215
  • 1
  • 7