[Biopython] extending Motif class

Bartek Wilczynski bartek at rezolwenta.eu.org
Fri Apr 1 01:07:53 UTC 2011


Hi,

On Fri, Apr 1, 2011 at 1:49 AM, Philip Machanick <philip.machanick at gmail.com
> wrote:

> I want to add a new scoring function to the Motif class and in true
> object-oriented spirit would like to do it by deriving a new class rather
> than hacking the existing code.
>
> Well, if you want to keep your code separate from biopython and ba able to
use it with newer versions than maybe yes, but if you think tha your code
code be contributed to biopython and useful for other people, than I'd
consider just contributing via github.


> The general structure of my test program (all in 1 file) is:
>
> from Bio.Motif import Motif
>
> class ScannableMotif(Motif):
>    def pwm_score_hit(self,sequence,position):
>    ## stuff to compute my new score
>
> That's OK, although I suspect something fishy is hidden in your code here.
more later.


> from Bio import Motif
>

you shouldn't need to do that


> def main ():
>    for motif in
> ScannableMotif.parse(open("/Users/philip/tmp/meme.txt"),"MEME"):
>        for i in range(3):
>          print
> motif.pwm_score_hit("CCTGGGGTCCCATTTCTCTTTTCTCTCCTGGGGTCCC",i)
>
> is ScannableMotif now a module? or is "parse" a class method? BTW, you can
parse MEME files with Bio.Motif.parse...



> The two different imports appear to be necessary. I need the first to be
> able to use the base class to derive a new one, and without the second when
>
Yes, you need to import the module to subclass Motif


> I use metaclass methods, I get
>
> TypeError: Error when calling the metaclass bases
>    module.__init__() takes at most 2 arguments (3 given)
>
> I cannot reproduce this error and it is highly unlikely that it is related
at all to the Biopython code as Bio.Motif does not uses any metaclasses. I
think you cause it by something in your later code:


> The other problem: I can't directly invoke a metaclass method on a derived
> instance as above. The snippet below works as expected, but looks like a
> kludge to me. Is there a better way of accessing metaclass methods from a
> derived class object?
>
>    for motif in Motif.parse(open("/Users/philip/tmp/meme.txt"),"MEME"):
>        motif.__class__ = ScannableMotif # promote to the new class
>

There you go! don't do this. It is not the way objects "get promoted" to
other classes, You seem to be playing with some python internals here

       for i in range(3):
>          print
> motif.pwm_score_hit("CCTGGGGTCCCATTTCTCTTTTCTCTCCTGGGGTCCC",i)
>
> I think I have the class vs. metaclass concept straight but understanding
> why I need the two different flavours of import would be useful.
>
Don't get me wrong, but I don't think you _need_ any metaclasses here. I
think your problem is that you are trying to change the class of an existing
instance, which (while probably possible in python) is absolutely not the
way to go. If your code is able to produce the correct output using the
complicated imports it's interesting, but probably not the easiest way to
achieve it. However it's hard to say, what exactly is your goal from the
code you provided.

But, on the more constructive side of things, if you want to subclass
Bio.Motif and add a new method to it, you can just do what you did in the
beginning of your code (provided that you do not mess with m.__class__ or
something)
Then, your problem seems to be that the MEME parser fails to return your
subclass and gives you a Bio.Motif.Motif vanilla class (or MEMEMotif). What
you can do (if you insist on not adding the method to Bio.Motif.Motif), is
to write a constructor able to create a ScannableMotif from a "normal"
motif:

class ScannableMotif(Motif):
   def new_score_hit(self,sequence,position):
       return 1 # or something smarter...
   def __init__(self,m): #just copy it all...
       self.instances = m.instances
       self.has_instances=m.has_instances
       self.counts = m.counts
       self.has_counts=m.has_counts
       self.mask = m.mask
       self._pwm_is_current = False
       self._log_odds_is_current = False
       self.alphabet=m.alphabet
       self.length=m.length
       self.background=m.background
       self.beta=m.beta

And then you can do things like:
m=Bio.Motif.parse(f,"AlignAce")
s=ScannableMotif(m)
s.new_score_hit(seq,pos)

I hope this helps...



> --
> Philip Machanick
> Rhodes University, Grahamstown 6140, South Africa
> http://opinion-nation.blogspot.com/
> +61-7-3871-0963 mobile +61 42 234 6909 skype philipmach
> _______________________________________________
> Biopython mailing list  -  Biopython at lists.open-bio.org
> http://lists.open-bio.org/mailman/listinfo/biopython
>
>


-- 
Bartek Wilczynski



More information about the Biopython mailing list