exhuma
exhuma

Reputation: 21707

What happens when you access a class-variable from inside an instance with a self-reference?

One of my colleagues wrote a code similar to this:

class A(object):
    foo = "bar"


class B(A):
    def baz(self):
        print self.foo

and against my personal belief, this worked! I come from a mainly Java background, and this hurts my eyes... Personally, I would have written it like this:

class A(object):
    foo = "bar"


class B(A):
    def baz(self):
        print A.foo  # or dynamically determine the superclass.

I understand, that in Python variable names are often compared to "labels". But this still leaves a sour taste in my mouth. What are the implications of writing code like this? Is it really a bad idea? Can something go wrong?

The only "bad" thing I can imagine is that deeper down the class hierarchy, an instance variable may shadow the class variable... so, you could says it's... okayish?

Upvotes: 2

Views: 245

Answers (4)

Jan Vorcak
Jan Vorcak

Reputation: 19989

You can basically do both.

If your class has just one parent, you can reference the variable directly with the name.

class A(object):
    foo = "bar"


class B(A):
    def baz(self):
        print self.foo  

What you have done make sense if you use multi

class A(object):
    foo = "bar"

class A2(object):
    foo = "bar 2"

class B(A, A2):
    def baz(self):
        #print foo # would throw NameError: global name 'foo' is not defined
        #print self.foo # prints "bar"
        print A.foo # prints "bar"
        print A2.foo # prints "bar 2"  

EDIT: If we ignore that fact that Java doesn't have multiple inheritance, I think it behaves in the similar way.

public class A {
    String foo = "bar";
}

public class B extends A {

    public void baz() {
        System.out.println(this.foo);
    }

}
public static void main(String[] args) {
    B b = new B();
    b.baz(); // prints "bar"
}

The only difference is that in Java it is accessible with this.foo, super.foo but also foo, while in Python you can just use self.foo or <superclass>.foo, but not foo

Upvotes: 5

Peter DeGlopper
Peter DeGlopper

Reputation: 37319

This is kind of a complicated question. Your two code examples have slightly different behavior.

In your colleague's implementation, it is possible for an instance of B to have its own value of foo. Nothing in the above code would establish one, but you could easily do so:

example_B = B()
example_B.foo = 'foobar'

Then example_B.baz() would return 'foobar'.

If implemented with your version, example_B.baz() would not check the instance's value. Instead, it would return 'bar', regardless of what had been set on example_B.

In practice, you should usually be able to tell when designing your class whether a name should refer to instance-specific information or to class-specific information. Only class-specific information should be declared at the class level.

Some people use class-level declarations as a shortcut to ensure that a value is available on all instances of a class even if they don't set it, but I find that this usually leads to confusion as soon as they declare a mutable variable as a "default value":

class BadExample(object):
    accidentally_shared_value = []

Instead, you should generally declare instance-specific values in __init__:

class BetterExample(object):
    def __init__(self):
        self.unshared_value = []

Accidentally sharing mutable values is the main situation that can go wrong when misunderstanding what's a class attribute versus what's an instance attribute.

Upvotes: 0

Alex Parakhnevich
Alex Parakhnevich

Reputation: 5172

The only "bad" thing I can imagine is that deeper down the class hierarchy, an instance variable may shadow the class variable

It's actually possible without any deep hierarchy at all.

class TestClass(object):
    var = 'foo'
    def __init__(self):
        self.var = 'bar'

x = TestClass()
print x.var
>>> 'bar'
del x.var
print x.var
>>>> 'foo'

But it's okay in Python.

Upvotes: 0

Daniel Roseman
Daniel Roseman

Reputation: 599600

Well, first of all, Python is not Java. Some things are just different.

According to the semantics of Python, name resolution in classes has to work that way. Otherwise there'd be no way of calling methods from an instance, since methods are also class attributes.

You're right that an instance variable elsewhere could shadow the class variable. But in my opinion, that's a feature, not a bug. If you really wanted to be sure you were always accessing the class variable, you could do self.__class__.foo, but I don't think there's any need.

Upvotes: 3

Related Questions