DNAClass (Recombinatron) - Python

From SnOwy - Ed's Wiki Notebook

Jump to: navigation, search

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__()
Personal tools
Namespaces
Variants
Actions
Navigation
Toolbox