Junchao Gu Junchao Gu - 6 months ago 31
Python Question

Python super method: class name not defined

I have a class defined inside another class like this. Basically I am trying to override the

save
method in db.Model--it actually just the
django.db.models.Model
. But when I run this block of code, I see the an
NameError
.

class GameCenterDB:
class GameCenterDBConfig:
class Config:
db_for_read = "game_center_db.slave"
db_for_write = "default"

class PublisherTab(GameCenterDBConfig, db.Model):
publisher_id = db.PositiveIntegerField(primary_key=True)
name = db.CharField(max_length=100)
create_time = db.PositiveIntegerField()
update_time = db.PositiveIntegerField()

class Meta:
db_table = u'publisher_tab'

def save(self, *args, **kwargs):
curr_time = int(time.time())
if not self.create_time:
self.create_time = curr_time
self.update_time = curr_time
# See the line below, this triggers an error
# NameError: global name 'PublisherTab' is not defined
super(PublisherTab, self).save(*args, **kwargs)


According to my understanding, when it is inside GameCenterDB, I should be able to use PublisherTab directly right?

NameError: global name 'PublisherTab' is not defined


Change
save
method like this will solve the error. But I just do not understand why.

def save(self, *args, **kwargs):
curr_time = int(time.time())
if not self.create_time:
self.create_time = curr_time
self.update_time = curr_time
super(GameCenterDB.PublisherTab, self).save(*args, **kwargs)


Also, seems that
class PublisherTab(GameCenterDBConfig, db.Model):
is interpreted without any error and the mixin worked. Why
GameCenterDBConfig
can be used without any problem?

Answer

"According to my understanding, when it is inside GameCenterDB, I should be able to use PublisherTab directly right?"

Wrong. Python requires full qualification of class members with either the class or variable (typically 'self') prefix. This is true of any member variable declared within a class. E.g.:

class Foo:
    class Bar:
        quux = 1
    def f(self):
        print "Foo.Bar.quux: %d" % Foo.Bar.quux
        print "self.Bar.quux: %d" % self.Bar.quux
foo = Foo()
foo.f()

Now consider this example:

# scope is top-level module
class Foo:
   # scope is Foo
    class Bar:
        # scope is Foo.Bar
        quux = 1
    # scope is Foo
    Bar.quux = 2 # [A]
    try:
        print "x: %d" % x
    except NameError:
        print "x gave an error because it is outside scope"
    def f(self):
        # scope is Foo when we are defining but not when we are running!
        try:
            print "Bar.quux: %d" % Bar.quux
        except NameError:
            print "Bar.quux gave us an error because it is outside scope"
        print "Foo.Bar.quux: %d" % Foo.Bar.quux
        print "self.Bar.quux: %d" % self.Bar.quux
        print "x is in scope: %d" % x
# scope is top-level module again
x = 456
foo = Foo()
foo.f()

I've added the code at [A]. The program now prints "2" not "1".

Why don't you need to qualify Bar.quux at [A] but you do inside f()?

Because when [A] is run, the script is inside the scope of class Foo.

But when foo.f() is run, the script is inside the scope of the module because that is where you are calling it from. That's why you need to explicitly declare self in the method definition, and foo.f() is syntactic sugar for Foo.f(foo).

This is one of the less pleasing parts of Python. It makes sense is hard to understand.

Comments