0

I need variable instances that by default have tuple behavior. Initially it must be exactly (1,1) That's why I've created the class that inherits from the tuple class. But a new created object initially equals to (,) (not (1,1) as I need).

I have following code but it doesn't work properly as I want.

class FooClass(tuple):

    def __init__(self):
        self = (1,1)

I need that new created objects works like this:

t = FooClass() # t must be (1,1) now
t = t[:-1] + (t[-1]+1,)
#t now must be (1,2)

What I do wrong?

Victor Shytiuk
  • 119
  • 1
  • 2
  • 8
  • 6
    Take a look at this answer: http://stackoverflow.com/questions/1565374/subclassing-python-tuple-with-multiple-init-arguments – RickyA Sep 05 '13 at 10:24
  • 1
    you are rebinding `self` within the `__init__` function; that does not alter the object `self` referred to before you rebound it. You'll need to use a `__new__` method here because by the time `__init__` is called, the object is *already* created and immutable. – Martijn Pieters Sep 05 '13 at 10:30
  • Remember that python doesn't have *variables* and memory locations as in other languages. It has identifiers and *bindings*. doing `self = (1, 2)` does *not* change the contents of the memory location called `self`. It *rebinds* the identifier `self` to a *new object* `(1, 2)`, without touching the original `self` object. *If* that was the only reference to that object then it would be garbage collected, but nothing else can happen. – Bakuriu Sep 05 '13 at 11:02

2 Answers2

3

You are rebinding self within the __init__ method; that does not alter the object self referred to before you rebound it. You simply pointed the local variable at a new object instead.

However, tuples are immutable, and so are their subclasses. You cannot alter self anymore by the time __init__ is being called.

You'll need to use a __new__ method here to influence how the instance is created instead; __new__ is the constructor, __init__ is an initializer:

class FooClass(tuple):
    def __new__ (cls):
        return super(FooClass, cls).__new__(cls, (1,2))

Demo:

>>> class FooClass(tuple):
...     def __new__ (cls):
...         return super(FooClass, cls).__new__(cls, (1,2))
... 
>>> t = FooClass()
>>> t[:-1] + (t[-1]+1,)
(1, 3)

Note that the last expression results in a tuple again; slicing the object still returns tuples:

>>> type(t)
<class '__main__.FooClass'>
>>> type(t[:-1])
<type 'tuple'>

Depending on what you are trying to do, you may need to override some of the container methods.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • I'd point out that implementing `__new__` is "not enough". In fact, in your example the object returned by the last expression is a `tuple`, not a `FooClass`. If the `FooClass` you wrote was enough, then the OP could simply use a function `create_tuple(*args)` instead, without having to complicate his life with classes. If this is not the case then he'll probably have to reimplement most of the methods implemented by `tuple`. – Bakuriu Sep 05 '13 at 11:05
  • @Bakuriu: indeed, I've updated the answer to point that out. – Martijn Pieters Sep 05 '13 at 11:09
0

Be careful, self is a reference to the object. By doing

self = (1, 1)

you are just assigning the variable self to a new tuple that will soon be garbage collected. Self is not a proper python keyword, you could call it "mike" or "something"

Thomas
  • 8,306
  • 8
  • 53
  • 92