1

I am trying to implement a very basic Java-like Dimension-Class in Python:

class Dimension(object):
    def __init__(self, width, height):
        self.__width = width
        self.__height = height

    @property
    def width(self):
        return self.__width
    @property
    def height(self):
        return self.__height
d = Dimension(800, 400)

print d.width
print d.height

This works fine: d.width returns an int=800, d.height returns an int=400. But how do I define, that d should return a tuple=(800,400) instead of <__main__.Dimension object at 0xXXXXXXXX>?

Is there an in-built class function similar to __repr__ for it? How do I define the returned value of a custom class? Or do I have to implement a new type?

Thank you

TerrorToaster
  • 13
  • 1
  • 5
  • You can make `__init__` return a tuple `width, height` but I guess that's not what you want, since you can't use all the instance methods then. If you want to implement conversion to a tuple then you should implement the `__iter__` method: `return (self.width, self.height)`. – a_guest Jan 17 '17 at 14:58
  • `d` doesn't *return* anything; that is what it *is*. (Try the same thing with a function.) Methods & functions return values when called; you don't call a class. If you want a method to call to get this type of value, add it to your class. – Scott Hunter Jan 17 '17 at 15:02
  • 1
    @ a_guest I don't know what you expect that to do, but `__init__` **must** return `None`, or you'll get `TypeError` when you try to create an instance of the class. For your example, you'd get `TypeError: __init__() should return None, not 'tuple'`. – PM 2Ring Jan 17 '17 at 15:13
  • BTW, `@property` should only be used when you need to evaluate some expression. Don't use it when a simple attribute lookup will suffice. Python isn't Java. Only create getters or setters when you really need them. – PM 2Ring Jan 17 '17 at 15:17
  • On a similar note, don't use leading double-underscore names like `.__width` unless you really need to invoke Python's name-mangling machinery. If you need private attributes (which you probably don't in this case), use a single leading underscore. And if you need to ask if you need Python's name-mangling machinery in a particular case, you probably don't. ;) – PM 2Ring Jan 17 '17 at 15:20
  • Found this: http://stackoverflow.com/a/7456865/7430966 Thank you for mentioning it – TerrorToaster Jan 18 '17 at 05:15

3 Answers3

4

If you want your class to behave as a tuple, inherit from tuple:

class Dimension(tuple):

then override the __new__ method to manipulate the creation:

def __new__(cls, width, height):
    tuple.__new__(cls, (width, height))

Leaving you with:

class Dimension(tuple):

    def __new__(cls, width, height):
        return tuple.__new__(cls, (width, height)) # create tuple with 2 items

    def __init__(self, *args):
        self.width = args[0] # width is the first argument passed
        self.height = args[1] # height is the next

>>> d = Dimension(800, 400)
>>> print(d)
(800, 400)
>>> print(d.width)
800
>>> print(d.height)
400
Zach Gates
  • 4,045
  • 1
  • 27
  • 51
  • I thought inheriting from tuple would be a nice idea, so I suggested it... but now its looks like an overkill and some black magic :) Nevertheless, this solution is quite nice, depending on the use case – DiKorsch Jan 17 '17 at 15:13
  • I agree, it's exactly what I searched for, but it looks really unconventional. Second opinion on this? – TerrorToaster Jan 18 '17 at 05:17
2

The closest thing that i can think of to what you described is using namedtuples

In [1]: from collections import namedtuple

In [2]: Dimension = namedtuple("Dimension", ('width', 'height'))

In [4]: d = Dimension(800, 400)

In [5]: d.width
Out[5]: 800

In [6]: d.height
Out[6]: 400

In [7]: d
Out[7]: Dimension(width=800, height=400)

In [8]: tuple(d)
Out[8]: (800, 400)

In [11]: w, h = d

In [12]: w, h
Out[12]: (800, 400)

Does this work for you? You could find more info about namedtuples here: https://docs.python.org/2/library/collections.html#collections.namedtuple

emre.
  • 1,296
  • 11
  • 13
0

I suggest implementing __iter__ for conversion to tuple:

class Dimension:
    # Other code goes here.

    def __iter__(self):
        return iter([self.width, self.height])

Note that I made width and height direct attributes of Dimension instead of using @property.

Example:

>>> d = Dimension(1, 2)
>>> tuple(d)
(1, 2)
a_guest
  • 34,165
  • 12
  • 64
  • 118