Orange Forum • View topic - Learner, Classifier and new-style classes.

Learner, Classifier and new-style classes.

A place to ask questions about methods in Orange and how they are used and other general support.

Learner, Classifier and new-style classes.

Postby Guest » Wed Aug 18, 2004 17:30

Just curious, will there be any problems if I want to write code like that of the tutorial:
Code: Select all
def Learner(examples=None, **kwds):
    learner = apply(Learner_Class, (), kwds)
    if examples:
        return learner(examples)
    else:
        return learner

class Learner_Class:
    def __init__(self, name='discretized bayes'):
        self.name = name

    def __call__(self, data, weight=None):
        disc = tablen=orange.Preprocessor_discretize( \
            data, method=orange.EntropyDiscretization())
        model = orange.BayesLearner(disc, weight=weight)
           
        return Classifier(classifier = model)
with new-style classes instead, like:
Code: Select all
class Learner(object):
    def __new__(cls, examples=None, **kwds):
        learner = object.__new__(cls, **kwds)
        if examples:
            return learner(examples)
        else:
            return learner

    def __init__(self, name='discretized bayes'):
        self.name = name

    def __call__(self, data, weight=None):
        disc = tablen=orange.Preprocessor_discretize( \
            data, method=orange.EntropyDiscretization())
        model = orange.BayesLearner(disc, weight=weight)
           
        return Classifier(classifier = model)
These should give pretty much identical behavior... but if your code depends on old-style classes in some way, then this code might fail somewhere...

Postby Janez » Fri Aug 20, 2004 15:50

Thanks, that's great!

We used the function-and-class trick prior to Python 2.2, and when the new-style classes came, it hasn't occurred to us that the __new__ operator can be used for constructing instances of various classes. (Although that is exactly what the C++ part of Orange does!)

We'll go through the documentation (some day) and simplify it. There's another thing in the example that needs to be fixed: all learners written in Python should be derived from orange.Learner. This is not strictly necessary, but it's nicer. Instead of 'object' (from your code fragment), we shall use orange.Learner.

Orange does not depend on old-style classes, in fact, its interface to Python was completely reprogrammed when the new classes came. Orange's classes are written in C++ but exported to Python as a hierarchy of new-style classes.

There are still things though, which one would expect to work but they don't. The problem is that Orange is internally a collection of C++ classes that know nothing about Python above them (I only use Python's garbage collection - no reason not to). The interface to Python is already complex and tricky, yet some things just cannot be done without making the Orange core Python-aware. Some limitations are described in the Reference Guide, the page on "Subtyping Orange Classes in Python".


Gosh, although I programmed the Orange's interface to Python, I have to confess I'm often wondering why and how some things work. I was surprised when I saw your code, replaced 'object' with 'orange.Learner' and thus called orange.Learner.__new__ - and it still worked... Wierd; I was sure it would find some excuse to crash.

Thanks,
Janez

Postby Guest » Wed Sep 29, 2004 21:23

Lets assume that the code below (same as the posted version, with object replaced by orange.Learner, as suggested) is called with examples != None. Then won't the line "return learner(examples)" result in the __call__ method being invoked before __init__ is invoked? In this example, it doesn't really matter, since nothing being done in __init__ is necessary for __call__. But in general, this might be a problem. Is there an easy way around this?


class Learner(orange.Learner):
def __new__(cls, examples=None, **kwds):
learner = orange.Learner.__new__(cls, **kwds)
if examples:
return learner(examples)
else:
return learner

def __init__(self, name='discretized bayes'):
self.name = name

def __call__(self, data, weight=None):
disc = tablen=orange.Preprocessor_discretize( \
data, method=orange.EntropyDiscretization())
model = orange.BayesLearner(disc, weight=weight)

return Classifier(classifier = model)

Postby Janez » Sat Jun 18, 2005 15:01

(OK, I'm not proud to answer this post 9 months later...)

The solution is simple: __new__ must call learner.__init__ itself before calling return learner(examples). Python will call the classifier's __init__.

Here's a simple code that does nothing useful, it's a wrapped Bayes learner that returns a wrapped Bayes classifier, and augmented with a few prints to show that everything is called in the correct order.

Thanks again for the idea!

Code: Select all
class Learner(orange.Learner):
    def __new__(cls, examples=None, weight = 0, **kwds):
        self = orange.Learner.__new__(cls, **kwds)
        if examples:
            self.__init__(**kwds)
            return self.__call__(examples, weight)
        else:
            return self

    def __init__(self, **kwds):
        print "Learner.__init__"

    def __call__(self, examples, weight=0):
        print "Learner.__call__"
        return Classifier(orange.BayesLearner(examples, weight))

class Classifier(orange.Classifier):
    def __init__(self, classifier):
        print "Classifier.__init__"
        self.classifier = classifier

    def __call__(self, example, rw=0):
        return self.classifier(example, rw)


Return to Questions & Support



cron