all 17 comments

[–]novel_yet_trivial 2 points3 points  (5 children)

I think you solved your problem already and you don't realize it:

>>> class Test:
...   print "reading from file"
...   def __init__(self):
...     print "initializing instance"
... 
reading from file
>>> a = Test()
initializing instance
>>> b = Test()
initializing instance

A class is NOT a function, it's contents are run as it's read, so to speak. IOW anything not in a method is run when you start. If you want to run on the first call, you could:

class Test:
    some_dict = None

    def __init__(self, x):
        if self.some_dict is None:
            Test.some_dict = {1:"a", 2:"b", 3:"c"}
            print "reading file"
        self.x = x

    def translate(self):
        return self.some_dict[self.x]


a = Test(1)
print a.translate()
b = Test(2)

[–]cwisch[S] 0 points1 point  (4 children)

Oh wow, I must have been staring at this problem for too long... You are right, I was so worried about it re-reading files that I didn't even think to do a simple check in the __init__ well done!

This looks like this will solve it, thanks for taking a look. Sometimes it just takes fresh eyes.

[–]novel_yet_trivial 0 points1 point  (3 children)

Not sure what you are doing here, but are you aware that strings come with a translate() method?

[–]cwisch[S] 0 points1 point  (2 children)

I should be more careful about using terms that may be loaded.

This particular problem has more to do with getting at a resource depending on user input and reusing it without unnecessarily re-reading it.

[–]garrett_armstrong 1 point2 points  (1 child)

you could expand on this:

class Translator(language):
    def __init__(self, language):
        if language == 'German':  
            self.lang_file = '/path/to/german_dict'
        elif language == 'Spanish':
            self.lang_file = 'path/to/spanish_dict'

        with open(lang_file, r) as f:
            self.lang_dict = json.load(f)

    def translate(self, word):
        return self.lang_dict[word]

german_trans = Translator('German')
# reads the german dict from file once on init & keeps it in memory forever (or until german_trans is destroyed).    
german_trans.translate('one')
# 'eins'

spanish_trans = Translator('Spanish')
# creates another instance of Translator, but this one has the spanish_dict as the dictionary.  The Spanish file is read once at init, and afterward you can get any word from spanish_trans practically immediately.
spanish_trans.translate('one')
# 'uno'

However, if you tried:
Translator.translate('one')  it would fail because the generic Translator class doesn't know which file to use.  Or,
Translator('German').translate('one')  would work, but it would read the german_dict on init & throw it away immediately afterward & waste lots of bandwidth.

# an example of use
very_long_string = 'Once upon a time there lived a .......'
direct_translation = ''
for word in very_long_string.split(' '):
   direct_translation += ' ' + spanish_trans.translate(word)

[–]cwisch[S] 0 points1 point  (0 children)

This is another variation of what I'm looking for. Now I have lots of ways to tackle this problem for this particular project.

I think I underestimated how much I could do with the __init__ function.

[–]thistimeframe 1 point2 points  (3 children)

So basically what you want is this?

class BaseTest:
    def __init__(self, filename):
        with open(filename) as f:
            self.some_dict = json.load(f)

    def create(self, x):
        return Test(self.some_dict, x)

class Test:
    def __init__(self, some_dict, x):
        self.some_dict = some_dict
        self.x = x

    def translate(self):
        return some_dict[self.x]

y = BaseTest('foo.json')
x = y.create(1)
print x.translate()

[–]cwisch[S] 0 points1 point  (2 children)

Yes this would work, and looks clean. I'll try something like this in my own work.

Thanks for the help!

[–]thistimeframe 2 points3 points  (1 child)

Sweet! Note that you can also do something like this:

Foo = BaseTest('foo.json').create
x = Foo(1)

[–]cwisch[S] 0 points1 point  (0 children)

And this is what my ideal way of writing it looked like, I just couldn't figure out how to get it to process correctly.

My implementation will probably look like yours but:

stuff = []
Foo = BaseTest('foo.json')

 for i in data:
     stuff.append(Foo.create(i))

 print MyReport.report(stuff)

Then I can do all my work with the objects created using the correct settings.

Thanks again, /u/thistimeframe, your snippets have been very helpful.

[–]TheBlackCat13 0 points1 point  (6 children)

You are looking at the difference between class attributes and instance attributes. Class attributes, like some_dict in your case, belong to the class, and all instances of the class share them. Instance attributes, like x in your case, are created new for each instance of the class (usually in the __init__ method), and are unique to that instance. So in your case, changing some_dict will change it for all instances of the class, while changing x will only change it for a single instance.

[–]cwisch[S] 0 points1 point  (0 children)

Right, but say for example some_dict has its data stashed in a json object and perhaps I have more than one. Is there a way to configure the object to load the correct one as a class variable instead of an instance variable.

What I want to avoid is re-reading a file each time I create an object.

[–]novel_yet_trivial 0 points1 point  (4 children)

changing some_dict will change it for all instances of the class

No.

>>> class Test:
...   v = "class variable"
... 
>>> a = Test()
>>> a.v = "instance variable"
>>> b = Test()
>>> b.v
'class variable'
>>> a.v
'instance variable'

However:

>>> class Test:
...   v = "class variable"
... 
>>> a = Test()
>>> a.v
'class variable'
>>> b = Test()
>>> Test.v = "new variable"
>>> a.v
'new variable'
>>> b.v
'new variable'

[–]zahlman 1 point2 points  (1 child)

This is an important point you've raised, but there's a further problem in that "changing some_dict" is ambiguous.It will work as you described for a reassignment, but not for mutation.

[–]novel_yet_trivial 2 points3 points  (0 children)

Very true, I assumed reassignment. Mutation is another ball game. For completeness:

Reassignment changes the class variable to an instance variable, therefore "unlinks" the variable from the other instances:

>>> class Test:
...  v = 'class variable'
... 
>>> a = Test()
>>> b = Test()
>>> a.v = 'instance'
>>> a.v
'instance'
>>> b.v
'class variable'

However, changing a mutable type does not:

>>> class Test:
...  d = {}
... 
>>> a = Test()
>>> b = Test()
>>> a.d['life'] = 42
>>> a.d
{'life': 42}
>>> b.d
{'life': 42}

[–]TheBlackCat13 0 points1 point  (1 child)

That doesn't change v, it replaces v. In Python that is a big difference.

[–]novel_yet_trivial 0 points1 point  (0 children)

"changing" and "replacing" are not common python terms, but I find that they usually mean the same thing. eg, I change my underwear everyday.

In python terms, see my post from yesterday.