0

I want to share data across different instances of a class, but the data must be provided externally the first time the class is created.

I have written the snippet below.

class Foo(object):

    _config = False
    eggs = None

    def __init__(self, spam, eggs=None):

        if Foo._config:
            # Assume initialized and eggs exists
            print(Foo.eggs)

        else:
            if eggs is None:
                raise ValueError('eggs must be provided the first time')
            else:
                Foo.eggs = eggs
                Foo._config = True
                print("Scrambled {}?".format(Foo.eggs))

        self.spam = spam
        print("Class variable - eggs: {}".format(Foo.eggs))
        print("Instance variable - spam: {}".format(self.spam))

which seems to work ...

>>>Foo._config
False
>>>a = Foo('chicken', 'eggs')    
Scrambled eggs?
Class variable - eggs: eggs
Instance variable - spam: chicken
>>>Foo._config
True

and the second time doesn't raise an error and shares the class variable

>>>b = Foo('duck')
eggs
Class variable - eggs: eggs
Instance variable - spam: duck

My question is whether this is a good approach? I have seen this question which suggests that including things in __init__ that are only called once is a bad idea, and I should use a metaclass?

My justification is that eggs will actually contain a very large pandas dataframe that I don't to repeat with each instance.

drstevok
  • 715
  • 1
  • 6
  • 15
  • 1
    Why don't you just initialize with the same exact large pandas data-frame? The cost of a reference is negligible. – juanpa.arrivillaga Jul 19 '17 at 22:31
  • Do you want all instances of the class to share the same panda dataframe, e.g. modifying it from one instance will modify for all? – Erich Jul 19 '17 at 22:33
  • @juanpa.arrivillaga Excuse my ignorance, but if the dataframe is `df` then calling `self.df=df` within `__init__` will reference the same dataframe each time? – drstevok Jul 19 '17 at 22:34
  • @Erich exactly that, yes – drstevok Jul 19 '17 at 22:35
  • @drstevok yes, if you pass the same `df` to `Foo`... that is *always how Python assignment works*. Please read [this](https://nedbatchelder.com/text/names.html) to assuage your fears. – juanpa.arrivillaga Jul 19 '17 at 22:39
  • You may want to use the [singleton pattern](https://en.wikipedia.org/wiki/Singleton_pattern). [SO](https://stackoverflow.com/q/31875/7216865) – Maurice Meyer Jul 19 '17 at 22:50
  • @juanpa.arrivillaga thanks for the link, it did help – drstevok Jul 19 '17 at 22:50
  • @drstevok please consider accepting an answer if the issue has been solved. This to close the question. – tupui Aug 19 '17 at 12:42

2 Answers2

1

I would advise against using the class namespace. see:

class holder():
    x = 5
    def __init__(self):
        self.x = 6
        return;

alpha = holder()
beta=holder()
beta.x = 4
holder.x = 100
print(holder.x)
print(alpha.x)
print(beta.x)

> 100
> 6
> 4

The scope of the variable gets diluted very quickly. I would reserve the class namespace for constants.

If you attempt to set a reference in the class namespace then you will have to generate the panda dataframe before. It will likely be easier to genereate it somewhere in your code before creating objects and then pass it by reference to each class.

As mentioned by @juanpa.arrivillaga : self.df = df

Erich
  • 1,902
  • 1
  • 17
  • 23
0

One way to do it is to create a @classmethod which you would call at the beginning in order to instantiate your constant values shared by all objects.

tupui
  • 5,738
  • 3
  • 31
  • 52
  • How would I ensure this runs only once? Is there a class equivalent to `__init__` – drstevok Jul 19 '17 at 22:43
  • Not that I am aware of. In our codebase we have a class which needs this behavior so we created an `initialize` classmethod. As you check with your `if egg is None`, we check that the classmethod has been called at least once. – tupui Jul 19 '17 at 22:45
  • @drstevok yes, there *is* a class-equivalent to `__init__`, the *metaclass* __init__ – juanpa.arrivillaga Jul 19 '17 at 23:30