Question

I have the following code

class Transcription(object):
    WORD = 0
    PHONE = 1
    STATE = 2

    def __init__(self):
        self.transcriptions = []

    def align_transcription(self,model,target=Transcription.PHONE):
        pass

The important part here is that I would like to have a class member as default value for a variable. This however gives the following error:

NameError: name 'Transcription' is not defined

Why is this not possible and what is the right (pythonic) way to do something like this.

Was it helpful?

Solution

You can't access it because Transcription isn't defined at the time that the def statement is running.

 def align_transcription(self,model,target=PHONE):
        pass

will do the trick. The PHONE name is available in the namespace which will become the Transcription class after the class statement finishes executing.

The way this works is that class is a statement that actually gets run. When Python encounters a class statement, it enters a new local scope and executes everything that's indented under the class statement as if it were a function. It then passes the resulting namespace, the name of the class and a tuple of the baseclasses to the metaclass which is type by default. The metaclass returns an instance of itself which is the actual class. None of this has occurred when the def statement executes.

The statement

class Foo(object):
    a = 1
    def foo(self):
        print self.a

creates a namespace ns = {'a': 1, 'foo': foo} and then executes

Foo = type('Foo', (object,), ns)

This is equivalent to

def foo(self):
    print self.a

Foo = type('Foo', (object,), {'a': 1, 'foo': foo})

You can clearly see that Foo is not defined at the time that foo is being defined so Foo.a makes no sense.

OTHER TIPS

The class is not associated with its name until the end of the definition.

The way I would write this (and I can't guarantee that it's Pythonic) is:

class Transcription(object):
    WORD = 1   # zero won't work below... need to change the value
    PHONE = 2
    STATE = 3

    def align_transcription(self, model, target=None):
        target = target or Transcription.PHONE
        # or
        if target is None: target = Transcription.PHONE

Alternatively, setting PHONE to zero instead of WORD will be fine. The if statement will work regardless of the values of the constants, but the or will replace a zero value with PHONE.

Other options are to define the constants in another class or to bind the align_transcription method outside of the class:

class Transcription(object):
    WORD = 0
    PHONE = 1
    STATE = 2

def _unbound_align_transcription(self, model, target=Transcription.PHONE):
    pass

Transcription.align_transcription = _unbound_align_transcription

The Transaction class does not exist yet when you define the align_transcription method, but PHONE is available in the scope. So, you can do either this way:

class Transcription(object):
    WORD = 0
    PHONE = 1
    STATE = 2

def __init__(self):
    self.transcriptions = []

def align_transcription(self,model,target=PHONE):
    pass

or with a target=None default if you plan to override PHONE in subclasses or instances:

def align_transcription(self,model,target=None):
    if target is None:
        target = self.PHONE
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top