next up previous contents
Next: Esercizio Up: Class and objects Previous: Class and objects   Indice

Classe DNA, e metodi relativi

Vediamo adesso un esempio più esplicito. Definiremo la classe DNA a partire dai metodi e attributi più semplici in modo incrementale. Innanzi tutto definiamo la classe e scriviamo un file di nome DNAClass.py che contiene

class DNA:
    """Class representing DNA as a string sequence."""  
    def __init__(self, s="", name=""):
        """Create DNA instance initialized to string s."""
        self.seq = s # set the sequence
        self.len=len(self.seq) # set the sequnce lenght
        self.name=name # set the name
Con questo avremo definito l'oggetto la classe DNA, se vogliamo creare un oggetto è sufficiente
>>> from DNAClass import DNA # importo la classe
>>> dna=DNA('acg','my') # creo un oggetto. Equivalente a usare DNA(s='acg',name='my')
>>> type(dna)
<type 'instance'>
>>> dir(dna)
['len', 'name', 'seq']
>>> dna.len, dna.name, dna.seq # attributi
(3, 'acg', 'my')
Come si vede dall'esempio precedente, quando definiamo un metodo della classe (compreso il metodo costruttore __init__), il primo parametro necessario è il riferimento all'oggetto stesso self. Quando però utiliziammo il singolo metodo i parametri che noi eventualmente passeremo saranno dal secondo in poi. Nell'esempio del costruttore con dna=DNA('acg','my') abbiamo creato un oggetto dna invocando il metodo __init__, in cui il primo parametro 'acg' è stato assegnato a s ed il secondo 'my' a name.

Possiamo iniziare a definire alcuni metodi, per esempio per avere il contenuto degli attributi, oppure per inserirlo. In questo caso aggiungiamo al metodo __init__ anche i seguenti

    def getname(self):
        """Return the name of the sequence."""
        if self.name:
            return self.name
        else:
            return 'unknown'

    def getsequence(self):
        """Return the dna sequence."""
        return self.seq

    def setname(self,name):
        """Set the name of the sequence."""
        self.name=name

    def setsequence(self,s):
        """Set the sequence content."""
        self.seq = s
        self.len=len(self.seq)
Possiamo vedere allora come si utilizzano i metodi della classe
>>> from DNAClass import DNA
>>> dna1 = DNA('CGACAAGGATTAGTAGTTTAC','mydna1')
>>> dna2 = DNA('gcctgaaattgcgcgc')
>>> dna1.getname()
'mydna1'
>>> dna2.getname()
'unknown'
>>> dna1.getsequence()
'CGACAAGGATTAGTAGTTTAC'
>>> dna2.getsequence()
'gcctgaaattgcgcgc'
>>> dna3=DNA()
>>> dna3.getsequence()
''
>>> dna3.getname()
'unknown'
>>> dna3.setsequence('gcctgactgg')
>>> dna3.setname('dna3')
>>> dna3.getname()
'dna3'
>>> dna3.getsequence()
'gcctgactgg'
Se però adesso volessimo introdurre metodi più utili, come ad esempio metodi per la trascrizione, il complemento ecc., ci rendiamo conto che è molto scomodo avere sia lettere maiuscole che lettere minuscole. A questo proposito, scegliendo di usare sempre le lettere minuscole, potremmo utilizzare un metodo definito da Python per le stringhe, lasciando l'arbitrarietà a chi istanzia di utilizzare indifferentemente maiuscole o minuscole, visto che saranno i metodi della classe ad occuparsene. Dovremo quindi cambiare i due metodi __init__, setsequence come
# ... 

    def __init__(self, s="", name=""):
        """Create DNA instance initialized to string s."""
        self.seq = s.lower()
        self.len=len(self.seq)
        self.name=name
# ... 

    def setsequence(self,s):
        """Set the sequence content."""
        self.seq = s.lower()
        self.len=len(self.seq)
adesso infatti avremmo che

>>> dna = DNA('CGACAAGGATTAGTAGTTTAC','mydna')
>>> dna.getname()
'mydna'
>>> dna.getsequence()
'cgacaaggattagtagtttac'
Prendiamo adesso in esame i due metodi: transcribe che trascrive dal DNA all'rna, e reverse che restituisce la sequenza di dna in ordine inverso. Una loro implementazione può essere
# ... 
    def transcribe(self):
        """Return as rna string."""
        return self.seq.replace('t', 'u')

    def reverse(self):
        """Return dna string in reverse order."""
        letters = list(self.seq)
        letters.reverse()
        return ''.join(letters)
da cui l'utilizzo immediato diviene
>>> dna = DNA('CGACAAGGATTAGTAGTTTAC','mydna')
>>> dna.getsequence()
'cgacaaggattagtagtttac'
>>> dna.transcribe()
'cgacaaggauuaguaguuuac'
>>> dna1.reverse()
'catttgatgattaggaacagc'
Adesso dovrebbe iniziare ad essere più chiaro l'utilità dell'uso degli oggetti, infatti è sicuramente più comprensibile
    rna=dna.transcribe()
piuttosto che
    rna='CGACAAGGATTAGTAGTTTAC'.lower().replace('t', 'u')

Ci farebbe comodo anche un metodo che ci desse lo strand complementare ossia data la nostra sequenza, vorremmo il complemento e poi l'inverso della sequenza. A questo scopo possiamo implementare i due metodi complement e reversecomplement. Per poterli utilizzare è inoltre utile introdurre anche il dizionario proprio della classe basecomplement che contiene il complemento. Inoltre potrebbe essere utile sapere qual'è il contenuto relativo di G+C rispetto all'intera sequenza con un metodo che potremmo chiamare gc_percentage. Da cui abbiamo

class DNA:
    """Class representing DNA as a string sequence."""

    basecomplement = {'a': 't', 'c': 'g', 't': 'a', 'g': 'c'}

# ... 
    def complement(self):
        """Return the complementary dna string."""
        letters = list(self.seq) # string to list
        # now for each base in letters take the basecomplement 
        letters = [self.basecomplement[base] for base in letters]
        # rejoin without spaces '' the complement 
        return ''.join(letters)

    def reversecomplement(self):
        """Return the reverse complement of the dna string."""
        letters = list(self.seq)
        letters.reverse()
        letters = [self.basecomplement[base] for base in letters]
        return ''.join(letters)

    def gc_percentage(self):
        """Return the percentage of dna composed of G+C."""
        s = self.seq
        gc = s.count('g') + s.count('c')
        return gc * 100.0 / len(s)
dove abbiamo fatto uso del costrutto di creazione di una lista [v[x] for x in in list]. Possiamo poi utilizzarle
>>> dna = DNA('CGACAAGGATTAGTAGTTTAC','mydna')
>>> dna.getsequence()
'cgacaaggattagtagtttac'
>>> dna.complement()
'gctgttcctaatcatcaaatg'
>>> dna.reversecomplement()
'gtaaactactaatccttgtcg'
>>> dna.gc_percentage()
38.095238095238095
Infine possiamo implementare il metodo della traduzione dal dna alla proteina, considerando l'intera sequenza come traducibile (cDNA). Implementiamo perciò il metodo translate. Bisogna però ricordare che data una sequenza di DNA possiamo avere 6 possibili reading frame, che corrispondono alla traduzione partendo dalla prima, dalla seconda o dalla terza posizione della sequenza diretta e le corrispondenti della sequenza del segmento complementare di dna (reversecomplement). Esistono solo 6 possibilili reading frames, in quanto il possibile strand +4 (o -4) ripartirebbe come lo strand 1 (-1) eliminando semplicemente un codone di 3 lettere. In maniera più pittorica
Data la sequenza      5'-c-g-a-c-a-a-g-g-a-3'  
                         | | | | | | | | |
 strand complemetare    -g-c-t-g-t-t-c-c-t- 
  letto da <-           -t-c-c-t-t-g-t-c-g-
Possibili reading frames
     Diretti   
             1           c-g-a c-a-a g-g-a
             2             g-a-c a-a-g
             3               a-c-a a-g-g 
     complementari      
            -1           t-c-c t-t-g t-c-g
            -2             c-c-t t-g-t
            -3               c-t-t g-t-c
Per poter scrivere la nostra funzione di traduzione, abbiamo bisogno di avere un dizionario con la conversione del codice genetico. Potremmo quindi definire l'attributo standard che contiene il dizionario del codice standard del dna. Da cui
class DNA:
    """Class representing DNA as a string sequence."""

    basecomplement = {'a': 't', 'c': 'g', 't': 'a', 'g': 'c'}

    standard = { 'ttt': 'F', 'tct': 'S', 'tat': 'Y', 'tgt': 'C',
             'ttc': 'F', 'tcc': 'S', 'tac': 'Y', 'tgc': 'C',
             'tta': 'L', 'tca': 'S', 'taa': '*', 'tga': '*',
             'ttg': 'L', 'tcg': 'S', 'tag': '*', 'tcg': 'W',

             'ctt': 'L', 'cct': 'P', 'cat': 'H', 'cgt': 'R',
             'ctc': 'L', 'ccc': 'P', 'cac': 'H', 'cgc': 'R',
             'cta': 'L', 'cca': 'P', 'caa': 'Q', 'cga': 'R',
             'ctg': 'L', 'ccg': 'P', 'cag': 'Q', 'cgg': 'R',

             'att': 'I', 'act': 'T', 'aat': 'N', 'agt': 'S',
             'atc': 'I', 'acc': 'T', 'aac': 'N', 'agc': 'S',
             'ata': 'I', 'aca': 'T', 'aaa': 'K', 'aga': 'R',
             'atg': 'M', 'acg': 'T', 'aag': 'K', 'agg': 'R',

             'gtt': 'V', 'gct': 'A', 'gat': 'D', 'ggt': 'G',
             'gtc': 'V', 'gcc': 'A', 'gac': 'D', 'ggc': 'G',
             'gta': 'V', 'gca': 'A', 'gaa': 'E', 'gga': 'G',
             'gtg': 'V', 'gcg': 'A', 'gag': 'E', 'ggg': 'G'
    }
# ....
    def translate(self,frame=1):
        """ translate a DNA like cDNA sequence to a protein 
            possible frames 1,2,3,-1,-2,-3 
        """
        possibleframe=(1,2,3,-1,-2,-3)
        if frame not in possibleframe:
            frame=1 # first frame
        if frame < 0 :
            cdna=self.reversecomplement()
            frame=abs(frame)-1
        else:
            cdna=self.seq
            frame=frame-1
        code=self.standard
        prot = ""
        i=frame # starting frame
        while i <= len(cdna)-3: # while there are at least 3 letters
            prot += code.get(cdna[i:i+3], "?")
            i=i+3
        return prot
Come si è fatto sopra possiamo provare il metodo dall'interprete Python
>>> from DNAClass import DNA
>>> dna = DNA('CGACAAGGATTAGTAGTTTAC','mydna')
>>> dna.translate() # default frame = 1 
'RQGLVVY'
>>> dna.translate(1) # frame 1
'RQGLVVY'
>>> dna.translate(2) # frame 2
'DKD**F'
>>> dna.translate(3) # frame 3
'TRISSL'
>>> dna.translate(-1) # frame complement 1 
'VNY*SLW'
>>> dna.translate(-2) # frame complement 2
'*TTNPC'
>>> dna.translate(-3) # frame complement 3
'KLLILV'
L'intera classe diviene quindi

class DNA: 
    """Class representing DNA as a string sequence.""" 
 
    basecomplement = {'a': 't', 'c': 'g', 't': 'a', 'g': 'c'} 

    standard = { 'ttt': 'F', 'tct': 'S', 'tat': 'Y', 'tgt': 'C',
             'ttc': 'F', 'tcc': 'S', 'tac': 'Y', 'tgc': 'C',
             'tta': 'L', 'tca': 'S', 'taa': '*' , 'tca': '*',
             'ttg': 'L', 'tcg': 'S', 'tag': '*', 'tcg': 'W',

             'ctt': 'L', 'cct': 'P', 'cat': 'H', 'cgt': 'R',
             'ctc': 'L', 'ccc': 'P', 'cac': 'H', 'cgc': 'R',
             'cta': 'L', 'cca': 'P', 'caa': 'Q', 'cga': 'R',
             'ctg': 'L', 'ccg': 'P', 'cag': 'Q', 'cgg': 'R',

             'att': 'I', 'act': 'T', 'aat': 'N', 'agt': 'S',
             'atc': 'I', 'acc': 'T', 'aac': 'N', 'agc': 'S',
             'ata': 'I', 'aca': 'T', 'aaa': 'K', 'aga': 'R',
             'atg': 'M', 'acg': 'T', 'aag': 'K', 'agg': 'R',

             'gtt': 'V', 'gct': 'A', 'gat': 'D', 'ggt': 'G',
             'gtc': 'V', 'gcc': 'A', 'gac': 'D', 'ggc': 'G',
             'gta': 'V', 'gca': 'A', 'gaa': 'E', 'gga': 'G',
             'gtg': 'V', 'gcg': 'A', 'gag': 'E', 'ggg': 'G'
    }
 
    def __init__(self, s="", name=""): 
        """Create DNA instance initialized to string s.""" 
        self.seq = s.lower() 
        self.len=len(self.seq)
        self.name=name

    def getname(self):
        """Return the name of the sequence."""
        if self.name:
            return self.name
        else:
            return 'unknown'
     
    def getsequence(self):
        """Return the dna sequence."""
        return self.seq

    def setname(self,name):
        """Set the name of the sequence."""
        self.name=name

    def setsequence(self,s):
        """Set the sequence content."""
        self.seq = s.lower()
        self.len=len(self.seq)
     
    def transcribe(self): 
        """Return as rna string.""" 
        return self.seq.replace('t', 'u') 
     
    def reverse(self): 
        """Return dna string in reverse order.""" 
        letters = list(self.seq) 
        letters.reverse() 
        return ''.join(letters) 
     
    def complement(self): 
        """Return the complementary dna string.""" 
        letters = list(self.seq) 
        letters = [self.basecomplement[base] for base in letters] 
        return ''.join(letters) 
     
    def reversecomplement(self): 
        """Return the reverse complement of the dna string.""" 
        letters = list(self.seq) 
        letters.reverse() 
        letters = [self.basecomplement[base] for base in letters] 
        return ''.join(letters) 
     
    def gc_percentage(self): 
        """Return the percentage of dna composed of G+C.""" 
        s = self.seq 
        gc = s.count('g') + s.count('c') 
        return gc * 100.0 / len(s) 
 
    def translate(self,frame=1):
        """ translate a DNA like cDNA sequence to a protein 
            possible frames 1,2,3,-1,-2,-3 
        """
        possibleframe=(1,2,3,-1,-2,-3)
        if frame not in possibleframe:
            frame=1 # first frame
        if frame < 0 :
            cdna=self.reversecomplement()
            frame=abs(frame)-1
        else:
            cdna=self.seq
            frame=frame-1
        code=self.standard
        prot = ""
        i=frame # starting frame
        while i <= len(cdna)-3: # while there are at least 3 letters
            prot += code.get(cdna[i:i+3], "?")
            i=i+3
        return prot


next up previous contents
Next: Esercizio Up: Class and objects Previous: Class and objects   Indice
2004-11-02