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 nameCon 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.095238095238095Infine 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-cPer 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 protCome 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