11

I'm trying to make a ImageLoader class handle the loading and processing of image resources like this:

class ImageLoader:
    TileTable = __loadTileTable('image path', some other variable)

    @staticmethod
    def _loadTileTable(arg1, arg2):
        blah blah

however, on compile i get: NameError: name '_loadTileTable' is not defined

If i replace the second line with TileTable = ImageLoader.__loadTileTable('image path', some other variable) then i get NameError: name 'ImageLoader' is not defined

As i'm going from C# to Python, static classes with static methods is what i'd use to implement this. However, i'm open to how I'd do this in general in python (that is, call static library functions that are only grouped together by their functionality).

UPDATE: After reading both answers, I'm getting a picture that what i'm trying to do probably isn't right. How would I go about imlementing ImageLoader so that I can do this:

Assuming that tile table returned an array

module1.py

aTile = ImageLoader.TileTable[1]

module2.py

anotherTile = ImageLoader.TileTable[2]

ideally, i'd populate TileTable just once.

Update:

Thanks for all the answers, I found my last answer to populating TileTable just once in the python modules doco

"A module can contain executable statements as well as function definitions. These statements are intended to initialize the module. They are executed only the first time the module is imported somewhere"

As for static class, i'm going to forgo classes and just make a module level variable.

Joe
  • 11,147
  • 7
  • 49
  • 60
  • 2
    When you're using `staticmethod` in Python, you're usually doing something wrong. If you need neither instance variables nor class variables (static fields), just use a module-level function. –  Feb 15 '11 at 14:39
  • 1
    @delnan: I don't agree wholly. Case in point Python's default `dict.fromkeys()`. There are legit use cases for these things. – jathanism Feb 15 '11 at 14:45
  • 1
    @delnan: I disagree. Encapsulation is prime tenant of object-oriented programming, which Joe is attempting to use. Keeping a function that only relates to an ImageLoader within the ImageLoader class is an effective way to programmatically encode that relationship (rather than implicitly associate them because they're in the same module). – Jason R. Coombs Feb 15 '11 at 14:46
  • 1
    @jathanism @Jason R. Coombs: Yes, there are legit use cases. But they're rare. When there's a clear connection to the containing class, e.g. it serves as an alternative constructor with a helpful name, for example - but that's not the case here, the function is private, odds are it will never be called anywhere else. And if you're coming from a language where static methods are the *only* way to write instance-independent code, you should IMHO treat static methods as something to unlearn (because usually you don't need them in Python). –  Feb 15 '11 at 14:55
  • @Joe: "How would I go about imlementing ImageLoader so that I can do this:" This is a separate question. Close this -- which is on static methods. Open a new question with your **real** question in it. – S.Lott Feb 15 '11 at 16:06
  • @S.Lott i thought about that but then i'd be repeating the same question to explain the motivation and if i just linked to this q, it would seem like 1 question separated into 2 (from the new question's perspective). i see your point though just 50/50 about which way to go. i'd be more inclined to change the title to reflect my **real** question. – Joe Feb 15 '11 at 23:31
  • @Joe: You're new here. Let me explain how this works. People skim the title and looks for sensible, focused, easy-to-answer questions. Titles that don't match the question and long, complex histories with a lot of edits are too difficult to dig into. You're **real** question has almost **nothing** to do with this question. It's not going to look like a repeat, because your real question is short and simple. – S.Lott Feb 16 '11 at 03:28
  • @S.Lott ah ok, i guess from now on, i'll consider updates more carefully... would you have anything to add to my current dilemma w.r.t the updated question? i'm looking around to specifically answer my last point 'ideally, i'd populate TileTable just once.' the current solutions seems to call _loadTileTable more than once if i understand... – Joe Feb 16 '11 at 03:35

4 Answers4

5

In Python, the code in the class block is first executed, then the resultant namespace is passed to the class initializer. The code you wrote could have also been written as:

TileTable = _loadTileTable(arg1, arg2)
@staticmethod
def _loadTileTable(arg1, arg2):
    pass # blah blah
ImageLoader = type('ImageLoader', (), {'TileTable': TileTable, '_loadTileTable': _loadTileTable})
del TileTable
del _loadTileTable

As you can see, the call of _loadTileTable appears before the definition of it. In your example, within the class definition, the call to _loadTileTable must come after the definition of _loadTileTable.

One possible fix is to simply re-arrange the class definition.

class ImageLoader:
    def _loadTileTable(arg1, arg2):
        pass # blah, blah

    TileTable = _loadTileTable('image path', other_var)

Note that I removed the 'staticmethod', because at the point where _loadTileTable is called, it's being called as a function and not a method. If you really want it to be available after class initialization, you can define it as a static method after the fact.

class ImageLoader:
    def _loadTileTable(arg1, arg2):
        pass # blah, blah

    TileTable = _loadTileTable('image path', other_var)

    _loadTileTable = staticmethod(_loadTileTable)
Jason R. Coombs
  • 41,115
  • 10
  • 83
  • 93
  • Your sample code won't actually work because of the method call inside of the class that requires arguments that cannot be passed. – jathanism Feb 15 '11 at 14:43
  • jathanism: I updated the answer to match the changes to the question (which assumes the parameters to _loadTileTable are in `globals()`) – Jason R. Coombs Feb 15 '11 at 14:51
  • I'm not sure I understand you fully. Your second code block does work, i hadn't tried removing the staticmethod decorator the first time. However this means that i'd have to worry about the order of declaration of my variables/methods. Is there a way like in c# that i can declare a variable and use a method within the class and not have to worry about the order of invocation? – Joe Feb 15 '11 at 14:54
  • @Joe: No. Python is dynamic and treats functions as first-class objects - from that follows that functions are only created and associated with a name when execution flow hits the line `def ...`. But since the *body* of functions isn't executed directly, it only matters if you mix declarations with statments to be executed directly (which is, for that very reason, rarely done). Declaring two functions calling each other is fine, as long as you only call either after both are defined. –  Feb 15 '11 at 14:58
  • i've updated the question to ask what i'm really after, now that i understand that i have no idea how to write this in python – Joe Feb 15 '11 at 15:04
  • @Joe: Being a former C++ programmer, I think I gleaned from your initial question what you were seeking. My answers still apply to your updated question. – Jason R. Coombs Feb 15 '11 at 15:48
5

Answering just the updated question, what you would do in Python is make TileTable a variable called tile_table in a module called imageloader. There is no reason at all to put any of this inside a class.

So then you get:

module1.py

import imageloader
aTile = imageloader.tile_table[1]

module2.py

import imageloader
anotherTile = imageloader.tile_table[2]

and imageload.py looks something like:

def _loadTileTable(arg1, arg2):
    pass # blah blah
tile_table = _loadTileTable('image path', other_var)

Think of a Python module as a singleton instance in other languages (which in fact it is) and you'll be able to reconcile this with any OO preconceptions you inherited from other languages.

Duncan
  • 92,073
  • 11
  • 122
  • 156
  • i like the simplicity of your answer but doesn't this mean that everytime i call tile_table, it would execute the _loadTileTable function again which would unnecessarily load the same thing? So what i'd probably want in this case is a function to load it into a module level variable and then use that variable. – Joe Feb 15 '11 at 23:12
  • No, the code in imageload.py only executes once, the first time it is imported (oops, just noticed it should be imageloader.py!). Subsequent imports don't execute the code again, they just re-use the existing module. – Duncan Feb 16 '11 at 21:16
3

Class-level variables which get updated are a bad, bad thing. Our default expectation is that object instances are stateful and classes are stateless.

In this case, we're trying to "magically" initialize a collection as a class variable, which is a toweringly bad idea. A collection is simply an object with simple instance-level attributes.

The magical Tile Table should not be a concealed, static part of the ImageLoader. There is no possible reason for that. It should be an argument to the ImageLoader if you want to avoid loading it more than once.

Separating these promotes testability. It's not arbitrary. It's how unit testing gets done.

What you want is this.

class ImageLoader( object ):
    def __init__( self, theTileTable ):
        self.tile_table= theTileTable

class TileTable( object ):
    def __init__( self, path, some_other_arg ):
         self.tileTable= self._loadTileTable( path, some_other_arg )
    def _loadTileTable(arg1, arg2):
         blah blah

No static anything. Independent units. More easily testable. No weird dependencies. No magic.

S.Lott
  • 384,516
  • 81
  • 508
  • 779
  • 1
    The class-level variable will never get updated, it's only retrieved. As the name suggests, it's a table of image tiles (retrieved from a file once). The imageloader class is there solely to manage the creation and storage of tile resources, i don't see the sense of creating another class to hold one method which logically would belong within the imageloader class. Lastly, to use this in two separate modules, i'd either have to create two instances or pass an imageloader reference around, both of which i mentioned in my question that i did not want to do. – Joe Feb 16 '11 at 11:31
  • Anyway, thanks for your input, i'm going to try what duncan suggested and see how i go. – Joe Feb 16 '11 at 11:33
  • @Joe: "i don't see the sense of creating another class to hold one method which logically would belong within the imageloader class." Testability. I believe the answer already said that. " to use this in two separate modules, i'd either have to create two instances or pass an imageloader reference around,". Sadly, passing the instance around is the correct answer, and is the most testable. Simply refusing to write testable code isn't really the best idea. – S.Lott Feb 16 '11 at 11:48
  • 1
    hum... this is getting a bit off topic, but even if i did want to test getting these static images from a static location, can't i just run the module by itself and see that it generates the images or not? or are you talking about the ability to mock/stub classes out that i can't do with module level variables? – Joe Feb 16 '11 at 11:57
  • @Joe: "the ability to mock/stub classes out that i can't do with module level variables". Precisely. That's why class-level magic variables which change ("retrieved from a file once" is a change) makes testing more complex than it needs to be. – S.Lott Feb 16 '11 at 12:00
  • 1
    ahh ok, point taken. are you saying that for testing, i shouldn't have any class or module level variables? i noticed that you do not mention module level variable, i'm assuming they are just as bad when it comes to testing as class variables? hum... i think for the time being, this will add too much complexity to my beginner's program but these are helpful pointers to pick up as i learn – Joe Feb 16 '11 at 12:10
  • @Joe: Module level variables aren't the design or testing nightmare that changeable class-level variables are. – S.Lott Feb 16 '11 at 13:04
2

Is there a design reason you're using a static method? If so, because you're not overloading the class initialization, you'll need to declare the variable after the method definition.

But, if you do this, you'lll get a new error:

NameError: name 'arg1' is not defined

The reason for this is because you're executing the method within the class before the class is even instantiated, therefore you never have a chance to pass the arguments to the method.

So, the proper way to do this is to overload the __init__() method so that assignment to TileTable only happens when the class is constructed:

class ImageLoader(object):
    def __init__(self, arg1, arg2):
        self.TileTable = self._loadTileTable(arg1, arg2)

    @staticmethod
    def _loadTileTable(arg1, arg2):
        print arg1, arg2

This gives you the ability to call ImageLoader._loadTileTable() without having an instance, but then it also allows you to create the TileTable instance variable upon creating an instance.

Using a Class method
In response to my comment about the possible need for a classmethod, here is an example that covers this:

class ImageLoader:
    @classmethod
    def _loadTileTable(cls, arg1, arg2):
        return arg1, arg2

# We're creating the class variable outside of the class definition. If you're doing 
# this in a module, no one will ever notice.
ImageLoader.TileTable = ImageLoader._loadTileTable('foo', 'bar')

There might be a better way to do this, I don't know. But I do think that this covers what you are looking for:

>>> i = ImageLoader()
>>> i
<__main__.ImageLoader instance at 0x100488f80>
>>> ImageLoader.TileTable
('foo', 'bar')
>>> i.TileTable
('foo', 'bar')

There you have an instance i that has access to the class variable, but ImageLoader.TileTable is still available from the class object without the need for an instance.

jathanism
  • 33,067
  • 9
  • 68
  • 86
  • I think should have pointed out that my goal was to create a static TileTable so it can be accessed by other classes. I don't want separate instances of ImageLoader floating around. the loadTileTable is meant to be a private function that does some work to load tiles when the TileTable is first used or when the static class is first initialised. So once when the app starts. – Joe Feb 15 '11 at 14:49
  • It sounds like you may need a `classmethod` then, which uses the class object as the first argument (where an instance method uses the instance as the first argument). Static methods are not bound to the class and therefore have no access to class variables. – jathanism Feb 15 '11 at 14:58
  • i did try changing the decorate to classmethod but it still told me that the method was undefined. – Joe Feb 15 '11 at 15:01
  • I updated the answer to use a classmethod, have a look. I'd like to see if this is helpful. :) – jathanism Feb 15 '11 at 16:15
  • so then calling ImageLoader.TileTable will just get redirected to ImageLoader._loadTileTable right? But that means everytime i call ImageLoader.TileTable, i'm going to reload the tile table like the other answers. What i'd like is to call _loadTileTable once for the life of the application. – Joe Feb 15 '11 at 23:25
  • I'm pretty sure, just like any variable assignment, that unless you re-declare the variable, any further references to it are just going to give to the return value of the assignment operation and will not execute the method/function again. – jathanism Feb 16 '11 at 16:20