7

For one of the project I am currently working I was thinking of creating a class that could not be instantiate by a client and only be supplied an instance of through a particular interface i.e. the client would not be able create further instance out of it by some hackery such as:

>>> try:
...     raise WindowsError
... except:
...     foo = sys.exc_info()
... 
>>> foo
(<type 'exceptions.WindowsError'>, WindowsError(), <traceback object at 0x0000000005503A48>)
>>> type(foo[2])()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: cannot create 'traceback' instances

once he has one.

I was successfully able to create a class that couldn't be instantiated. i.e.

>>> class Foo():
...     def __init__(self):
...         raise TypeError("cannot create 'Foo' instances")
... 
>>> bar = Foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in __init__
TypeError: cannot create 'Foo' instances
>>> bar
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'bar' is not defined

But how could I use this every same definition to create an instance of the class?

Of course I could do something like this:

>>> class Foo():
...     def __init__(self, instantiate = False):
...         if not instantiate:
...               raise TypeError("cannot create 'Foo' instances")

but I don't find it elegant enough nor does it completely prevent the client from further instantiating it. And no I aint going down the road of building a C++ module for it.

Any suggestions on how to achieve such a thing? import abc?

A brief rational to answer Martijn's question and for completeness:

Actual you could consider the instance of the particular, and related, classes, in question, as nodes in a tree and that both the parent and the children to remain connected, dependent on and cognizant of each other and have a single unique root throughout any instance of python(insured by the use package). Any state changes in a particular node would cause others to update themselves and the database to which they are connect, accordingly. Apart from that I was being curious to know how such a thing could be put in place (the traceback class was teasing me).

Community
  • 1
  • 1
Bleeding Fingers
  • 6,993
  • 7
  • 46
  • 74

1 Answers1

17

What you're doing is a bad idea, you shouldn't do it. I'm sure there's an other, better solution. If you do decide to go with your way anyways (you shouldn't), here's how you can create an object without using __init__():


Objects in python are created with the __new__() method. The method __init__() only edits the object which was created by __new__(). For example, __init__() usually initializes some attributes for the object.

When declaring something like x = Foo() what happens is this:

  1. x = object.__new__(Foo) gets called first and creates the object.
  2. Foo.__init__(x) gets called second, it simply initializes some attributes etc. to the already existing object.

This means that you are not required to call Foo() (and as a result, call __init__() too). Instead, you can just call __new__() directly:

class Foo(object):
    def __init__(self):
        raise TypeError("Cannot create 'Foo' instances.")

>>> x = object.__new__(Foo)
>>> x
<__main__.Foo object at 0x02B074F0>

Our x is now an instance of Foo, without any attributes that is, and it can use any methods defined in Foo class.

If you want, you can create your own replacement function of __init__ for initializing attributes:

def init_foo(foo, name):
    foo.name = name

>>> init_foo(x, "Mike")
>>> x.name
'Mike'

This could of course be Foo's instance method too:

class Foo(object):
    def __init__(self):
        raise TypeError("Cannot create 'Foo' instances.")

    def init(self, name):
        self.name = name

>>> x = object.__new__(Foo)
>>> x.init("Mike")
>>> x.name
'Mike'

Going even step further, you can even use a classmethod for creating your object with only one call:

class Foo(object):
    def __init__(self):
        raise TypeError("Cannot create 'Foo' instances.")

    @classmethod
    def new(cls, name):
        obj = object.__new__(cls)
        obj.name = name
        return obj

>>> x = Foo.new("Mike")
>>> x.name
'Mike'
Skamah One
  • 2,456
  • 6
  • 21
  • 31
  • Are you using python 3.x because on 2.6/2.7 if I do not inherit `object` in `Foo` I get `TypeError: object.__new__(X): X is not a type object (classobj)`. And FYI `traceback` resisted instantiation upon using your method. Appreciate your approach though. – Bleeding Fingers Apr 23 '13 at 19:06
  • @hus787 Yes, I'm on `python 3.3`, which is causing your error. I edited the answer so `Foo` inherits `object` now. – Skamah One Apr 24 '13 at 14:10
  • @hus787 Also, I'm pretty sure `traceback` is a class coded in C and cannot be recreated in python. You're asking for a class that cannot be instantiated, cannot be subclassed and YOU could still create it's instances. This is as good as you can get. – Skamah One Apr 24 '13 at 14:13
  • Since python has a bag full of goodies, I thought there might be a way to achieve such a thing and so I asked the question. I'll be doing some independent experiments on this before I can come to a conclusion. It should be non-instantiable by the client. Btw I had updated the question. – Bleeding Fingers Apr 24 '13 at 18:42
  • 3
    Could you please explain _why_ what @BleedingFingers is doing is a bad idea? I'm sure you're right, but unless you back up your statement with facts, it seems more like an opinion, and since we don't understand why it is bad we are less likely to heed your warning. – HelloGoodbye Nov 14 '18 at 06:45
  • It's a bad idea because it's against the Python ethos. It recognizes that trying to be too controlling tends to be the wrong thing to do. "We're all adults here", which is why Python doesn't have enforced/policed public/protected/private but a convention: If you are going to go against the expressed intent of something being hinted private then, go ahead. You know what you're doing. In the same vein: This is an OO language. Preventing people from instantiating things ties up the user of your code in ways that might be a problem to them due to your lack of imagination etc. Just don't do it. – W.Prins May 20 '22 at 19:09