Home > Design patterns, Python Language > Strategy pattern in python

Strategy pattern in python


Here is an example of strategy pattern in python. The alogrithm that varies in this example is the conversion of csv file to xml, html and any other formats. The client is the CsvUtil class. As you can see we can change the conversion at run time and also we can apply all the alogrithm if we wish to which we can’t achieve when we go for the regular inheritance model.  Please do note that an abstract class is equivalent to interface when there is no implementation.

Source code for all the classes related to strategy.

HTML_TEMPATE="""<html><body>%s</body></html>
"""
class CsvConverter:
	''' Abstract class or interface for conversion of csv file'''

	def convert(self,csv):
		pass #abstract method

class Csv2Html(CsvConverter):
	''' Covert Csv to html table with alternativing row colors'''

	def convert(self,csv):
		''' convert the csv file to html file '''
		htmltable = ''
		csvfile = open(csv)
		dialect = Sniffer().sniff(csvfile.read(2048))
		csvfile.seek(0) #rewind after sniffing
		csvreader = reader(csvfile,dialect)
		i = 0
		for line in csvreader:
			#print line
			if i == 0: #header row
					htmltable += '<tr class="head">'
			elif i % 2 == 0: #even row
					htmltable += '<tr class="even">'
			else: #odd row
					htmltable += '<tr class="odd">'
			for col in line:
				htmltable += '<td>%s</td>'%col
			htmltable += '</tr>'
			i += 1
		return HTML_TEMPLATE%htmltable

	def __str__(self):
		return 'Csv2Html'

class Csv2Xml(CsvConverter):
	''' Covert Csv to Xml '''

	def __init__(self,linetag='row'):
		''' Constructor '''
		self.linetag = linetag

	def convert(self,csv):
		''' convert the csv file to xml file '''
		print 'hello'
		xml = ''
		csvfile = open(csv)
		csvreader = DictReader(csvfile)
		for row in csvreader:
			xml += '<%s>'%self.linetag
			for colname,colvalue in row.iteritems():
				xml +=  '<%s>$%s</%s>'%(colname,colvalue,colname)
			xml += '</%s>'%self.linetag
		return xml

	def __str__(self):
		return 'Csv2Xml'

Here is the source code for the context. You can see the use of property in python here.

class CsvUtil(object):
	''' A wrapper or utility class for dealing with csv files '''

	def __init__(self,csv):
		''' constructor '''
		self.csv = csv
		self._numlines = None #this information is computed lazily
		self._numcols = None #this information is gathered lazily
		self._wellformed = None #this information is gathered lazily
		self._converter = Csv2Html() #default converter
	
	def _gatherstats(self):
		''' gather basic stats on the csv '''

		with open(self.csv) as csvfile:
			dialect = csv.Sniffer().sniff(csvfile.read(2048))
			csvfile.seek(0) #rewind
			reader = csv.reader(csvfile,dialect)
			self._numlines = 0
			self._wellformed = True
			self._numcols = 0
			for line in reader:
				self._numlines += 1
				if self._numcols != len(line):
					self._wellformed = False
	
	def convert(self):
		''' convert the csv file to another format '''

		return self._converter.convert(self.csv)
		
	def get_wellformed(self):
		''' a csv is wellformed if all the lines have same number of columns '''
		
		if not self._wellformed:
			self._gatherstats()
		return self._wellformed
	
	def get_numlines(self):
		''' number of lines in the csv files '''

		if not self._numlines:
			self._gatherstats()
		return self._numlines
	
	def get_numcols(self):
		''' number of lines in the csv files '''

		if not self._numcols:
			self._gatherstats()
		return self._numcols


	def get_converter(self):
		''' getter for converter '''
		return self._converter

	def set_converter(self,value):
		print 'set ',value
		self._converter = value

	#properties 	
	wellformed = property(get_wellformed)
	numlines = property(get_numlines)
	numcols = property(get_numcols)
	converter = property(get_converter,set_converter)

Finally the demonstration of using the context and strategy….

       csvfile = "C:\sandbox\python\mycsv.txt"
	util = 	CsvUtil(csvfile)  # my utility class for handling csv
	print util.numlines
	print util.wellformed
	print 'Default converter is',util.converter
	print util.convert() #print using the default converter 

	#now changing my converter at run time :-)
	util.converter = Csv2Xml()
	print 'My new converter is',util.converter
	print util.convert() 


  1. September 7, 2010 at 1:48 am | #1

    Strategy + Façade : here, the client class also has a convert method that is just a facade to Strategy.convert. Right ?

    • October 4, 2010 at 11:16 am | #2

      Yes you are right. Client class (CsvUtil) Convert method is the facade.

  2. March 3, 2012 at 12:16 pm | #3

    Hi.

    What is it “HTML_TEMPLATE”?

    where did you define this?

    Thank you

    • March 5, 2012 at 12:48 pm | #4

      HTML_TEMPLATE is just a string variable that defines the template of the html page. See the updated code..

  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.