1

I've come across two Stack Overflow posts that seem to offer conflicting answers:

Can anyone confirm whether or not storing instances of a class within a class variable of the same class is an acceptable Python design pattern?

3
  • 1
    For one thing, they ask two different questions: "Is it bad" and "How do I". This is a cross site duplicate of the first question you linked to. Mar 24, 2017 at 17:36
  • 2
    I'm voting to close this question as off-topic because it is a cross site duplicate of stackoverflow.com/questions/4831307/… Mar 24, 2017 at 17:37
  • @GregBurghardt - I tried opening this question on SO but was told that it was off topic and to move it here. The posts ask different questions, but the selected answers from both seem to offer conflicting advice.
    – atm
    Mar 24, 2017 at 22:20

2 Answers 2

2

Well, maybe that it's common to do in Python. However it's definitely not common in other languages and not a proper OOP way. (I don't want to start a language war - au contraire, Python is a very good language/environment for some use cases.)

There are times where you absolutely want to have track of instances of particular class. (DI containers, resources which are not managed by a garbage collector (if you have any) ...)

Usually it's solved by implementing a factory pattern. Here is quite a good article about it in Python:

http://python-3-patterns-idioms-test.readthedocs.io/en/latest/Factory.html

When a class instance is created, a reference to it can be put into a collection. There is a glitch, however (when using languages which have garbage collector or smart pointers). When somebody stops using the instance, your register still have a reference to the instance and therefore it's not garbage collected. You have to use either a weak reference (or it's smart-pointer equivalent).

Here is a Python doc for weak reference:

https://docs.python.org/3/library/weakref.html

So the code could look like this:

import weakref

shape_register = weakref.WeakValueDictionary()

class Shape(object):
    # Create based on class name:
    def factory(type):
        #return eval(type + "()")
        if type == "Circle":
            instance = Circle()
            shape_register[id(instance)] = instance
            return instance
        if type == "Square":
            instance = Square()
            shape_register[id(instance)] = instance
            return isinstance

        assert 0, "Bad shape creation: " + type
    factory = staticmethod(factory)

class Circle(Shape):
    def draw(self): print("Circle.draw")
    def erase(self): print("Circle.erase")

class Square(Shape):
    def draw(self): print("Square.draw")
    def erase(self): print("Square.erase")

And the use:

>>> s = Shape.factory("Circle")
>>> shape_register.valuerefs()
[<weakref at 0x00000000037830F8; to 'Circle' at 0x0000000003773C18>]

^ You can see that the Circle instance in in the register.

>>> s = Shape.factory("Circle")
>>> shape_register.valuerefs()
[<weakref at 0x0000000003759F10; to 'Circle' at 0x00000000037737F0>]

^ A new instance was created, you can no longer access the original one. It was garbage collected and doesn't show in the register anymore.

>>> c = Shape.factory("Circle")
>>> shape_register.valuerefs()
[<weakref at 0x0000000003759F10; to 'Circle' at 0x00000000037737F0>, <weakref at 0x00000000037830F8; to 'Circle' at 0x0000000003773C18>]
>>> 

^ However now another circle was created and stored in different variable. Both s and c are now accessible and the register shows two values.

1

Fundamentally, the answer in any language to this type of tradeoff will always be "it depends".

In Python, you get the preformed arguments from the Zen of Python.

  • You might argue it makes the code pretty, and 'Beautiful is Better than Ugly"
  • You might argue it isn't explicit, and "Explicit is Better than Implicit"
  • You might make many other arguments, appealing to the authority of the Zen of Python.

The point isn't to give a globally concrete answer that will be wrong in some cases; its to restrict the argument form to cause people to directly support an argument.

A good discussion will find new solutions that bridge several arguments. For example, would a decorator like @singleton_list be appropriate? Is in an error in your domain to initialize two objects with the same parameters? Which way is harder for consumers of code to understand? You want a solution for your problem domain.

It depends.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.