0

I need a small help with slots.

class bstream(object):
  __slots__ = ['stream']
  stream = string()

  def __new__(self, stream, encoding=None):
    if encoding == None:
      encoding = ENCODING['default']
    if isinstance(stream, bytes):
      self.stream = stream.decode(encoding)
    elif isinstance(stream, string):
      self.stream = stream
    else: # if unknown type
      strtype = type(stream).__name__
      raise(TypeError('stream must be bytes or string, not %s' % strtype))
    return(self)

  def __repr__(self):
    '''bstream.__repr__() <==> repr(bstream)'''
    chars = ['\\x%s' % ('%02x' % ord(char)).upper() for char in self.stream]
    result = ''.join(chars)
    return(result)

  def func(self):
    return(1)

Don't be confused with those string type and ENCODINGS dictionary: they are constants. The problem is that the following commands don't work as I expect:

>>> var = bstream('data')
>>> repr(var)
<class '__main__.bstream'> # Instead of '\\x64\\x61\\x74\\x61'
>>> var.func()
TypeError: unbound method func() must be called with bstream instance as first argument (got nothing instead)

What's wrong? I'd really like to leave my class immutable, so solutions with the removing of slots are really not very good ones. :-) Thanks a lot!

ghostmansd
  • 3,285
  • 5
  • 30
  • 44
  • Why? `__slots__` is fairly useless, especially until you know you need it. It's a poor way of making your class immutable, for that, just don't mutate it (or don't provide a public API to mutate it). – Julian Jul 27 '12 at 20:35
  • @Julian: You're right, I've defined it to make class immutable. Before I've always done the same thing using Cython, but it is not as much portable as Python. – ghostmansd Jul 27 '12 at 20:41
  • 1
    Why do you want the class to be immutable? Just don't change it if you don't want to. You're working pretty hard against Python. – Ned Batchelder Jul 27 '12 at 20:50
  • Take a look at [this answer](http://stackoverflow.com/a/472024/41747), you probably do not want `__slots__` here and I really don't think that you want [`__new__`](http://docs.python.org/reference/datamodel.html#object.__new__) either. – D.Shawley Jul 27 '12 at 20:50
  • @D.Shawley: it is already in Python 3. I use it because it is still the best way to avoid errors when users try to set attributes. It is also a good way to reduce memory which Python needs when it works with objects. – ghostmansd Jul 27 '12 at 20:55

1 Answers1

4

You want to use __init__, not __new__.

__new__ is a class method for which the first argument (self) is the class object, not the newly created object. It has to return the new object. You usually don't want to redefine it, but you can if you want to do things like return an existing object.

__init__ is a regular instance method, and the first argument (self) is the newly created instance. It works like constructors in other languages.

To fix this, change the method name to __init__ and delete the final line (return(self)). __init__. must always return None; returning anything else results in a TypeError.

Taymon
  • 24,950
  • 9
  • 62
  • 84
  • That raises an error on creating the object: `AttributeError: 'bstream' object attribute 'stream' is read-only` – ghostmansd Jul 27 '12 at 20:42
  • Ok, I've solved it: I just need to delete `stream = string()` right after `__slots__ = ['stream']`. Thanks! – ghostmansd Jul 27 '12 at 20:48