DNAClass (Recombinatron) - Python
From SnOwy - Ed's Wiki Notebook
Contents |
Abstract
The DNAClass is the core (atomic) datatype that is used in the Recombinatron software. Each instance (a DNAObject) encloses a string representation of a suite of tokens used in a loop of DNA and an implicit deterministic index per token. A DNAObject will be bagged together in some collection to be processed by each of the larger modules-- the Giant Scaffold Module, the Operators Module and the Filters Module.
DNAObjects are immutable (by contract) after construction, so that recombinations due on the enzyme operator (function) will create new DNAObjects-- immutability implies that the Giant Scaffold Module may implement a history.
API
Constructor
DNAObject = DNAClass(string DNAString)
The argument DNAString is used to specify what strand of DNA to enclose. Abstractly, this string's zeroth index is used as the DNAObject's zeroth index
Iterator Methods
Because this class is predominantly a container for (DNA) strings that offers iteration, essentially all of the methods used in its objects' lifecycles are generators. A DNAClass object (DNAObject) is itself an iterator, and behaves like an iteration over the enclosed string.
#The following...
for i in DNAObject:
print i
#Is the same as...
for i in DNAString:
print i
Two overloaded iterator functions are provided. One performs forward iteration and the other provides reverse compliment iteration.
#The use of forward() without arguments is the same as the above two examples.
for i in DNAObject.forward()
print i
#The use of reverse() provides a reverse complimented iteration.
for i in DNAObject.reverse()
print i
Providing arguments to these two functions change their behaviour. Arguments and their defaults are shown below.
DNAClass.forward(start = 0, enum = False) DNAClass.reverse(start = -1, enum = False)
Snapshot
A 20090626_155825 snapshot of the DNAClass source can be found here.
"""
DNAClass.py
"""
import types
reverse_compliment = {
"A" : "T", "C" : "G", "G" : "C", "T" : "A",
"P" : "p", "Q" : "q", "p" : "P", "q" : "Q",
"B" : "b", "D" : "d", "b" : "B", "d" : "D",
}
if __name__ == "__main__":
exit("Oops! Can't be run as a standalone.")
class DNAClass(object):
"""
A circular string class for use with Recombinatron, UWiGEM09
"""
class NotAvailable(Exception):
def __init__(self, message = "Method not available."):
Exception.__init__(self, message)
def __init__(self, strand=""):
object.__init__(self)
self.strand = str(strand)
#####
# Special abstract functions
#####
def __len__(self):
"""
len() abstract function - Python collections
"""
return len(self.strand)
def __iter__(self):
"""
for abstract function - Python collections / iterables
"""
for yieldObj in self.__iterguts(0):
yield yieldObj
def __getitem__(self, key):
"""
for abstract index syntax - Python collections / sequences
"""
if isinstance(key, slice):
if key.step != None:
raise self.NotAvailable, "Don't use 'steps' in a slice."
if(isinstance(key.start, int)):
self.strand[key.start] # Used to raise IndexError
start = key.start % len(self) # Fewer cases-- MODULUS MAGIC!
if(isinstance(key.stop, int)):
self.strand[key.stop] # Used to raise IndexError
stop = key.stop % len(self)
try: # for integers...
if start <= stop:
return self.strand[start:stop]
else:
return self.strand[start:] + self.strand[:stop]
except UnboundLocalError: # for nonetype...
return self.strand[key.start:key.stop]
elif isinstance(key, int):
return self.strand[key]
else:
raise TypeError, "Expected 'integer' or 'slice'."
#####
# Iterators
#####
def forward(self, start = 0, enum = False):
"""
for keyword (custom, with start token index)
"""
if not isinstance(start, int):
raise TypeError, "Expected argument 'start' to be integer"
if isinstance(start, bool):
raise TypeError, "Expected argument 'start' to be integer"
if not isinstance(enum, bool):
raise TypeError, "Expected argument 'enum' to be boolean"
for yieldObj in self.__iterguts(start, False, enum):
yield yieldObj
def reverse(self, start = -1, enum = False):
"""
for keyword (custom, reverse, with start token index)
"""
if not isinstance(start, int):
raise TypeError, "Expected argument 'start' to be integer"
if isinstance(start, bool):
raise TypeError, "Expected argument 'start' to be integer"
if not isinstance(enum, bool):
raise TypeError, "Expected argument 'enum' to be boolean"
for yieldObj in self.__iterguts(start, True, enum):
yield yieldObj
def __iterguts(self, start, reverse = False, enum = False):
self.strand[start] # Used to raise IndexError
cursor = start = start % len(self)
looking_for_last_token = False
if reverse:
def more_to_yield(): return cursor >= 0
def get_yieldObj():
try: token = reverse_compliment[self.strand[cursor]]
except KeyError: token = self.strand[cursor]
return token
increment = lambda x: x-1
reset = lambda x: len(self) -1
else:
def more_to_yield(): return cursor < len(self)
def get_yieldObj(): return self.strand[cursor]
increment = lambda x: x+1
reset = lambda x: 0
if enum:
inner_yieldObj = get_yieldObj
def get_yieldObj(): return (cursor, inner_yieldObj())
while True:
if cursor == start and looking_for_last_token:
raise StopIteration
if more_to_yield():
yield get_yieldObj()
cursor = increment(cursor)
looking_for_last_token = True
else:
cursor = reset(cursor)
#####
# Methods
#####
def indices(self, token):
"""
produces a list of integers starting at token
"""
acc = []
i = 0
while i < len(self):
if self.strand[i] == token:
acc += [i]
i += 1
return acc
def rc(self):
"""
returns a new reverse complimented object.
"""
acc = ""
for token in self.strand:
try: retChar = reverse_compliment[token]
except KeyError: retChar = token
acc = retChar + acc
return DNAClass(acc)
#####
# Special abstract functions that aren't available to anyone
#####
def __reversed__(self):
"""
reversed() keyword - Python collections / sequences
"""
raise self.NotAvailable, "Use DNAObject.reverse() instead."
def __setitem__(self, key, value):
"""
for abstract index syntax - Python collections / sequences
"""
raise self.NotAvailable, "Read-only -- cannot set tokens."
def __delitem__(self, key):
"""
for abstract index syntax - Python collections / sequences
"""
raise self.NotAvailable, "Read-only -- cannot delete tokens."
#####
# Special abstract functions that fall through to the list underneath
#####
def __eq__(self, other): return self.strand.__eq__(other.strand)
def __lt__(self, other): return self.strand.__lt__(other.strand)
def __le__(self, other): return self.strand.__le__(other.strand)
def __ne__(self, other): return self.strand.__ne__(other.strand)
def __gt__(self, other): return self.strand.__gt__(other.strand)
def __ge__(self, other): return self.strand.__ge__(other.strand)
def __repr__(self): return __name__ + ".DNAClass(\"" + self.strand + "\")"
def __str__(self): return self.strand.__str__()