Search code examples
pythonpython-3.xclassnameerror

Name error: 'self' not defined - when calling a function to create in-class variables


I have the following class:

class Documents:
    def __init__(self, input_file):
        self.input_file_ = input_file #List in which each element is a list of tokens
        
        assert type(self.input_file_) is list, 'Input file is not a list'
        assert type(self.input_file_[0]) is list, 'Elements in input file are not lists' #Only checks first instance, not all. But should suffice
                
    def get_vocabulary(self):
        vocabulary = set([el for lis in self.input_file_ for el in lis])
        return vocabulary, len(vocabulary) 
    
    vocabulary, vocabulary_size = self.get_vocabulary()

But when I try to execute it, I get the following error:

Traceback (most recent call last):

  File "<ipython-input-34-4268f473c299>", line 1, in <module>
    class Documents:

  File "<ipython-input-34-4268f473c299>", line 30, in Documents
    vocabulary, vocabulary_size = self.get_vocabulary()

NameError: name 'self' is not defined

This is a common error on SO. However, I have not found an answer in which the code has a similar structure.

Can someone explain to me why I get this error and how I can change my code so that I don't get an error?


Solution

  • The way you have it, vocabulary, vocabulary_size = self.get_vocabulary() is being executed when the class is being defined, so there is no self. The latter is the name of the first argument passed to methods of a class and is the instance of the class (that was previously created) on which to operate.

    The proper way to do this would be to call get_vocabulary() method from the __init__() method when an instance of the class exists and is being initialized.

    Here's what I mean:

    class Documents:
        def __init__(self, input_file):
            self.input_file_ = input_file # List in which each element is a list of tokens
            self.vocabulary, self.vocabulary_size = self.get_vocabulary()
    
            assert type(self.input_file_) is list, 'Input file is not a list'
            assert type(self.input_file_[0]) is list, 'Elements in input file are not lists' # Only checks first instance, not all. But should suffice
    
        def get_vocabulary(self):
            vocabulary = set([el for lis in self.input_file_ for el in lis])
            return vocabulary, len(vocabulary)
    

    Comment (off-topic):

    In languages that have classes and support object-orientated code like Python, it's usually best to avoid type-checking as much as possible because it doesn't support subtyping — but you can overcome that limitation when it is done by using the built-in isinstance() built-in function.

    This implies that it would probably be better to do the following in your __init__() method:

        assert isinstance(self.input_file, list), 'Input file is not a list'
        assert isinstance(self.input_file_[0], list), 'Elements in input file are not lists' # Only checks first instance, not all. But should suffice