OscarRyz
OscarRyz

Reputation: 199215

Explaining Python variable scope

I'm teaching myself Python and I was translating some sample code into this

class Student(object):
    def __init__( self, name, a,b,c ):
        self.name = name
        self.a = a
        self.b = b
        self.c = c

    def average(self):
        return ( a+b+c ) / 3.0 

Which is pretty much my intended class definition.

Later in the main method I create an instance and call it a:

if __name__ == "__main__" :
    a = Student( "Oscar", 10, 10, 10 )

That's how I find out that the variable a declared in main is available to the method average and to make that method work, I have to type self.a + self.b + self.c instead.

What's the rationale for this?

Upvotes: 8

Views: 1256

Answers (3)

Eli Courtwright
Eli Courtwright

Reputation: 192921

There are several reasons, though the main one is from the Zen of Python: "Explicit is better than implicit." In a language like C++, a method on the class always has an implicit argument this which is pushed onto the stack every time the method is called. In this case, when an instance variable b exists as well as a global variable b, then the user may just refer to b referring to one without realizing that the other will be used. So Python forces you to be explicit about your scope to avoid confusion.

With that being said, there are other reasons as well. For example, I may define a function outside of a class and then attach it to a class at runtime. For example:

def log(self):
    print "some library function requires all objects to have a log method"
    print "unfortunately we're using the Student class, which doesn't have one"
    print "this class is defined in a separate library, so we can't add the method"
    print "fortunately, we can just add the method dynamically at runtime"

Student.log = log

Here the fact that self is explicit makes it trivial for us to define a function outside of a class and then attach it to that class. I don't do this sort of thing incredibly often, but it's EXTREMELY useful when I do.

Here's an even more complex example; suppose we want to define a class inside another class, such as for the purposes of unit testing:

class SomeUnitTests(TestCase):
    def test_something(self):
        class SomeMockObject(SomeActualObject):
            def foo(self2):
                self.assertEqual(self2.x, SOME_CONSTANT)

        some_lib.do_something_with(SomeMockObject)

Here the presence of an explicit self (which we can call whatever we want, it doesn't have to be self) allows to to distinguish between the self of the inner and outer classes. Again, this isn't something I do frequently, but when I do then it's incredibly useful.

Upvotes: 2

Alex Martelli
Alex Martelli

Reputation: 881575

Barenames (like a, b, c) are always scoped as local or global (save for nested functions, which are nowhere around in your code). The rationale is that adding further scopes would needlessly make things more complicated -- e.g, if in your self.a = a the barename a could be scoped to mean what you appear to want (equivalent to self.a) then the assignment itself would be meaningless (assigning a name to itself), so you'd need further complicated rules.

Just using qualified names (like self.a) when you want something different than barenames' simple, straightforward, and optimized behavior, is by far the simplest approach -- perfectly workable, no complicated rules whatsoever, and allows the compiler to optimize things effectively (since e.g. a barename's scope is always lexically determined, not dependent on dynamically varying characteristics of the environment). So, besides perhaps nostalgia for other language with more complicated scoping rules, there's really no rationale for complicating the semantics of barenames.

Upvotes: 10

Danilo
Danilo

Reputation: 79

All instance variables should be called using self

Upvotes: -1

Related Questions