tags:

views:

425

answers:

3

Okay, it took me a little while to narrow down this problem, but it appears python is doing this one purpose. Can someone explain why this is happening and what I can do to fix this?

File: library/testModule.py

class testClass:

    myvars = dict()

    def __getattr__(self, k):
        if self.myvars.has_key(k):
            return self.myvars[k]

    def __setattr__(self, k, v):
        self.myvars[k] = v

    def __str__(self):
        l = []
        for k, v in self.myvars.iteritems():
            l.append(str(k) + ":" + str(v))

        return " - ".join(l)

test.py

from library import testModule

#I get the same result if I instantiate both classes one after another
c1 = testClass()
c1.foo = "hello"
c2 = testClass()

print("c1: " + str(c1) + "\n")
print("c2: " + str(c2) + "\n")

Output:

c1: foo:hello
c2: foo:hello

My best guess is that because library has an "__init__.py" file, the whole module is loaded like a class object and it's now become part of a lasting object.. is this the case?

+7  A: 

myvars is a property of the class, not the instance. This means that when you insert an attribute into myvars from the instance c1, the attribute gets associated with the class testClass, not the instance c1 specifically. Since c2 is an instance of the same class, it also has the same attribute.

You could get the behavior you want by writing this:

class testClass:
    def __init__(self):
        self.myvars = dict()

    def __getattr__(self, k):
        if self.myvars.has_key(k):
            return self.myvars[k]

    def __setattr__(self, k, v):
        self.myvars[k] = v

    def __str__(self):
        l = []
        for k, v in self.myvars.iteritems():
            l.append(str(k) + ":" + str(v))
        return " - ".join(l)
David Zaslavsky
Actually, assigning self.myvars = dict() in __init__() causes a recursion error. Heh, I think the solution is to ditch the __setattr__ and use the __dict__ var instead.
Ian
oh yeah, I didn't think about the recursion problems... you could work around that but it would indeed be better to use the builtin var __dict__. This is exactly what it's there for.
David Zaslavsky
That is probably wise. But it isn't the __setattr__ that's causing the recursion error. __setatrr__ calls __getattr__ which calls itself in the first line.
David Berger
+2  A: 

The other answers are correct and to the point. Let me address some of what I think your misconceptions are.

My best guess is that because library has an "__init__.py" file, the whole module is loaded like a class object and it's now become part of a lasting object.. is this the case?

All packages have an __init__.py file. It is necessary to make something a python package. That package may or may not have any code in it. If it does it is guaranteed to execute. In the general case, this doesn't have anything to do with how the other modules in the package are executed, although it certainly is possible to put a lot of really cool trickery in there that does affect it.

As for how modules and classes work, it is often a really good idea to think of a module as a class object that gets instantiated once. The loader executes the files once and all variables, class definitions, and function definitions that are available at the end of the file are then accessible as part of the module.

The same is true of classes, with the main exception that functions declared within classes are transformed into methods (and one special method let's you instantiate the class). So testModule has-a testClass has-a myvars. All three objects are unique: there will not be multiple instances of any of them. And the has-a relathionship is really more-or-less the same whether we say "module has-a class object" or "class object has-a class variable". (The difference being implementation details that you ought not be concerned with.)

David Berger
A: 

For a good reference on how to use getattr and other methods like it, refer to How-To Guide for Descriptors and there's nothing like practice!

Joel