1

I try to understand the following code from some OpenSource GitHub project. There is a class without __init__ but with a __new__ method. The code is given as:

class Node(object):
    #pylint: disable=W0404

    #Singleton-Pattern
    _instances = dict()

    def __new__(cls, name=None):
        """ Instanciates a node from a file, and (name=None) creates a new node 
            Caution: Filenames are always given relative to the root-dir
            When no name is given, a new node is created. """

        if(name!=None and cls._instances.has_key(name)):
            return(cls._instances[name])

        if(name==None):  # a new node, lets find a name
            for i in itertools.count(0):
                name = "node%.4d"%i
                if(cls._instances.has_key(name)): continue# new nodes might not been saved, yet
                if(path.exists("./nodes/"+name)): continue
                break


        self = object.__new__(cls)
        cls._instances[name] = self

        #actuall init-code
        from ZIBMolPy.pool import Pool #avoids circular imports
        self._pool = Pool() #Pool is a singleton
        self._name = name
        self._tmp = Store() #for thing that need to be stored temporarly
        self._obs = Store()
        self.parent = None

        if(path.exists(self.dir)):
            self.reload()

        #self.pool.append(self) #register with pool
        return(self)

    #---------------------------------------------------------------------------
    @property
    def obs(self):
        return(self._obs)

I found one discussion beween the __init__ method and the __new__ method at Python's use of __new__ and __init__? According to the highest rated comment, one should only use new if one is subclassing an immutable type like str, int, unicode or tuple. But I think here it is used for some other reason. Further more I don't understand why the class cls should have a name (and why it should have anything todo with some folders) and why I can call

n= Node()
n.obs

like the function obs would be a property function, but it is actually not..

I am confused. If your not, I cant wait for your respond.

Community
  • 1
  • 1
Adam
  • 25,960
  • 22
  • 158
  • 247

1 Answers1

3

This class uses __new__ to implement a singleton pattern.

__new__ produces the new instances for a class, but in this case it'll return an existing instance instead if the same name was used before. You cannot do this with __init__, because that is called after the instance has been created. Note that when cls._instances.has_key(name) is False, self = object.__new__(cls) is called to create a new instance of the class, which is then initialized and returned.

Why the class checks for existing paths in the ./nodes/ directory is not clear, that is an application-specific check that without further context cannot readily be exposed any further.

The @property decorator replaces a function with a python descriptor. When looking up an attribute from a class, Python will call it's __get__ method if the attribute has one. The Python expression n.obs is translated into type(n).obs.__get__(n, type(n)) by Python. A property object calls the wrapped function and returns the result when it's __get__ is called.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Thank you for your respond. So the only reason to use '__new__' is because sometimes I want to get a object which has been allready created right? This is only in the case 'if(name!=None and cls._instances.has_key(name))'. How should I possibly call the '__new__' function with name!=None? I mean evertime I create a Node-Object I call 'node=Node()' and then by default 'name=None'. Also I thought cls is a static global class for objects. So if I add a name to the global class cls than each object contains that name since each object is a subclass of cls - is that correct? – Adam May 03 '13 at 17:18
  • @Adam: See the [`__new__`](http://docs.python.org/2/reference/datamodel.html#object.__new__) documentation; `cls` is a reference to the class object passed into the `__new__` method (like `self` is passed into methods referring to the current instance). `Node()` creates a new name for you if you don't pass one in (`name=None`), theoretically this project can pass in an explicit `name` when it needs to. In this case `cls` would refer to the `Node` class or any subclasses of `Node`. – Martijn Pieters May 03 '13 at 17:22
  • @Adam: Python class objects usually (in Python 3, *always*), inherit from `object`, not `cls`. `cls` is just a local name for this method. – Martijn Pieters May 03 '13 at 17:23
  • @Adam: `object` itself is immutable, so you cannot add anything to it. – Martijn Pieters May 03 '13 at 17:24
  • Thank you for your respond. I just recognised that what I asked was very basic and wrong - sorry for that. But I somehow struggle with:`self = object.__new__(cls)`. Can I see somewhere what this method does? I guess it does the following: `return cls()` but then again __new__ of Node would call itself recoursivly? – Adam May 03 '13 at 18:30
  • @Adam: `object` is the *parent* class. If `Node.__new__` wasn't defined, the default implementation is `object.__new__`. Here `Node.__new__` *overrides* `object.__new__`, then calls it when needed. – Martijn Pieters May 03 '13 at 19:13