Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Adding a property to an int value in python

I have a value which is commonly used throughout my code in different units. E.g. representing a buffer size in number of BYTES, but in many places refering to the size as KB or as MB (this is just an example, not my real use case).

For elegance and ease of use, I want to avoid explicit conversions (e.g. size/1024, or b_to_mb(size)), because they are needed in so many different places.

I thought I can achieve that with properties, which makes the conversion easy (x.kb or x.mb), and also makes the caller unaware of the units of the actual value stored.

My code looks like this:

class BufferSize(int):
    @property
    def b(self):
        return int(self)
    @property
    def kb(self):
        return self.b / 1024
    @property
    def mb(self):
        return self.kb / 1024

This works, until I use the arithmetic operators:

s = BufferSize(500000)
s.kb
=> 488.28125   # ok
s.mb
=> 0.476837158203125  # ok
type(s + BufferSize(0))
=> int  # type lost...

Is there a way to ensure arithmetic operations preserve the type? That is, other than overriding each and every one of them?

Or maybe a different and better way to solve the original problem?

like image 897
shx2 Avatar asked Jan 07 '17 20:01

shx2


People also ask

How do you add properties in Python?

You can create a property by calling property() with an appropriate set of arguments and assigning its return value to a class attribute. All the arguments to property() are optional. However, you typically provide at least a setter function.

How do you add a new property to an object in Python?

Python provides a function setattr() that can easily set the new attribute of an object. This function can even replace the value of the attribute. It is a function with the help of which we can assign the value of attributes of the object.

What is %s in int in Python?

%s can format any python object and print it is a string. The result that %d and %s print the same in this case because you are passing int/long object. Suppose if you try to pass other object, %s would print the str() representation and %d would either fail or would print its numeric defined value.


1 Answers

Well, it's not so much re-implement as wrap/cast the results of int's magic methods, at least those that return ints.. All, of them, unfortunately. Wonder if you could not use a decorator or metaclass to expedite that somewhat.

class BufferSize(int):
    @property
    def b(self):
        return int(self)
    @property
    def kb(self):
        return self.b / 1024
    @property
    def mb(self):
        return self.kb / 1024

    def __add__(self, *args, **kwds):
        return BufferSize(super(BufferSize, self).__add__(*args, **kwds))

v = BufferSize(1) + BufferSize(2)

print v, type(v)

output:

3 <class '__main__.BufferSize'>

I also thought about adding the property directly to int itself, but a quick test ruled that out.

>>> int.b = "b"

output:

 can't set attributes of built-in/extension type 'int'
like image 169
JL Peyret Avatar answered Oct 16 '22 21:10

JL Peyret