5

I'm teaching myself (probably my first mistake) classes and methods by converting a sentiment analysis script to use them.

I thought I had all of the methods in place, but I keep getting

global name 'get_bigram_word_feats' is not defined

I'm sure I'd be getting an error for get_word_feats, too, if it got that far.

I'm banging my head against this one big-time. I tried removing staticmethod and adding self. What am I doing wrong?

Here's my code:

def word_feats(words):
    return dict([(word, True) for word in words])


class SentClassifier:

    def __init__(self, name, location):
        self.name = name
        self.location = location
        self.fullpath = location + "/" + name

    def doesexist(self):
        return os.path.isfile(self.fullpath)

    def save_classifier(self):
        rf = open(self.fullpath, 'wb')
        pickle.dump(self.fullpath, rf)
        rf.close()

    def load_classifier(self):
        sf = open(self.fullpath, 'rb')
        sclassifier = pickle.load(sf)
        sf.close()
        return sclassifier


class Training:

    def __init__(self, neg, pos):
        self.neg = neg
        self.pos = pos
        self.negids = open(self.neg, 'rb').read().splitlines(True)
        self.posids = open(self.pos, 'rb').read().splitlines(True)
        self.exclude = set(string.punctuation)
        self.exclude = self.exclude, '...'
        self.swords = stopwords.words('english')

    def tokens(self, words):
        words = [w for w in nltk.word_tokenize(words) if w not in self.exclude and len(w) > 1
            and w not in self.swords and wordnet.synsets(w)]
        return words

    def idlist(self, words):
        thisidlist = [self.tokens(tf) for tf in words]
        return thisidlist

    @staticmethod
    def get_word_feats(words):
        return dict([(word, True) for word in words])

    @staticmethod
    def get_bigram_word_feats(twords, score_fn=BigramAssocMeasures.chi_sq, tn=200):
        words = [w for w in twords]
        bigram_finder = BigramCollocationFinder.from_words(words)
        bigrams = bigram_finder.nbest(score_fn, tn)
        return dict([(ngram, True) for ngram in itertools.chain(words, bigrams)])

    @staticmethod
    def label_feats(thelist, label):
        return [(get_word_feats(lf), label) for lf in thelist]

    @staticmethod
    def label_grams(thelist, label):
        return [(get_bigram_word_feats(gf), label) for gf in thelist()]

    @staticmethod
    def combinegrams(grams, feats):
        for g in grams():
            feats.append(g)
        return feats

    def negidlist(self):
        return self.idlist(self.negids)

    def posidlist(self):
        return self.idlist(self.posids)

    def posgrams(self):
        return self.label_grams(self.posidlist, 'pos')

    def neggrams(self):
        return self.label_grams(self.negidlist, 'neg')

    def negwords(self):
        return self.label_feats(self.negidlist, 'neg')

    def poswords(self):
        return self.label_feats(self.posidlist, 'pos')

    def negfeats(self):
        return self.combinegrams(self.neggrams, self.negwords)

    def posfeats(self):
        return self.combinegrams(self.posgrams, self.poswords)

starttime = time.time()

myclassifier = SentClassifier("sentanalyzer.pickle", "classifiers")

if myclassifier.doesexist() is False:
    print "training new classifier"
    trainset = Training('data/neg.txt', 'data/pos.txt')
    negfeats = trainset.negfeats()
    posfeats = trainset.posfeats()
    negcutoff = len(negfeats) * 8 / 10
    poscutoff = len(posfeats) * 8 / 10

    trainfeats = negfeats[:negcutoff] + posfeats[:poscutoff]
    testfeats = negfeats[negcutoff:] + posfeats[poscutoff:]
    print 'train on %d instances, test on %d instances' % (len(trainfeats), len(testfeats))

    classifier = NaiveBayesClassifier.train(trainfeats)
    print 'accuracy:', nltk.classify.util.accuracy(classifier, testfeats)
    myclassifier.save_classifier()

else:
    print "using existing classifier"
    classifier = myclassifier.load_classifier()

classifier.show_most_informative_features(20)
mystr = "16 steps to an irresistible sales pitch, via @vladblagi: slidesha.re/1bVV7OS"
myfeat = word_feats(nltk.word_tokenize(mystr))
print classifier.classify(myfeat)

probd = classifier.prob_classify(myfeat)

print probd.prob('neg')
print probd.prob('pos')

donetime = time.time() - starttime

print donetime
theharshest
  • 7,767
  • 11
  • 41
  • 51
user1066609
  • 167
  • 1
  • 3
  • 8
  • I should point out - I know my code's probably hideous. My plan was to start consolidating/cleaning up once I got this working. Not the best technique, I know, but... – user1066609 Nov 23 '13 at 19:12
  • 4
    "(probably my first mistake)", no, on the contrary. The best way to learn something, including programming, is experimenting. Since computers are so forgiving (you're only rarely going to lose a limb when experimenting here), go wild! – Lasse V. Karlsen Nov 23 '13 at 19:19
  • Does this answer your question? [How can I call a function within a class?](https://stackoverflow.com/questions/5615648/how-can-i-call-a-function-within-a-class) – mkrieger1 Aug 27 '22 at 21:14

3 Answers3

3

All the information you need is in the exception message:

global name 'get_bigram_word_feats' is not defined

(my emphasis)

Python doesn't understand that you want to access that method from the class, since you did not specify the class name as part of the method invocation. As such, it is looking for the function in the global namespace and failed to find it.

If you recall from calling instance methods, you need to prefix the methods with self. to make the Python interpreter look in the right place, and this also holds for static methods though you do not specify self., instead you specify the class name.

So to fix this, prefix the call to the method with the class name:

return [(Training.get_bigram_word_feats(gf), label) for gf in thelist()]
         ^---+---^
             |
             +-- you need this part
Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
  • FWIW, a classmethod would make more sense than a staticmethod here - you'd get access to the class and would not have to hardcode the class name in `labelgrams`. As a general rule, either you want to tie the method to a class and then you make it a classmethod, or it really has no meaning being overridden and you just use a plain function (and yes, it's a "general" rule, ie a kind of a guideline if you don't know better). – bruno desthuilliers Nov 23 '13 at 19:46
1

global name 'get_bigram_word_feats' is not defined

Your call should look like this (note the class name being used here):

@staticmethod
def label_grams(thelist, label):
    return [(Training.get_bigram_word_feats(gf), label) for gf in thelist()]

In general, for static methods, use the class name.


I tried removing staticmethod and adding self. What am I doing wrong?

In that case, you'd use self.funcName(..). Something like below:

def label_grams(self, thelist, label):
    return [(self.get_bigram_word_feats(gf), label) for gf in thelist()]
UltraInstinct
  • 43,308
  • 12
  • 81
  • 104
1

Good news: the fix is simple. Call it this way: Training.get_bigram_word_feats(...).

E.g.,

@staticmethod
def label_grams(thelist, label):
    return [(Training.get_bigram_word_feats(gf), label) for gf in thelist()]
ron rothman
  • 17,348
  • 7
  • 41
  • 43