I'm still relatively new to python, 1-2 years of solo-learning, and am trying to improve my code structure so I'm refactoring some old programs I wrote. In one program, I defined a couple of methods for writing files. The first uses, "write" to dump a huge http-response. The second uses "writelines" to dump various derived lists, e.g. lists of links, or forms, or other extracted data.
I'd originally factored out the naming of the file:
@property
def baseFilename(self):
unacceptable = re.compile(r'\W+')
fname = re.sub(unacceptable,'-',self.myUrl)
t = datetime.datetime.now()
dstring = "%s%s%s%s%s%s" % (t.year, t.month, t.day, t.hour, t.minute, t.second)
fullname = fname + '_' + dstring + '.html'
return fullname
But I have a large redundant block of code in each write method:
def writeFile(self, someHtml, writeMethod=write, prefix="RESPONSE_"):
'''The calling functions will su开发者_Python百科pply only the data to be written and
static prefixes, e.g. "full_" for the entire http-response.
'''
fullpath = self.myDump + prefix + self.baseFilename
with open(fullpath, 'w') as h:
h.write(someHtml)
h.close()
print "saved %s" % fullpath
return fullpath
def writeList(self, someList, prefix="mechList_"):
'''Like write file but for one of the many lists outputted.
How do I refactor this, since redundant?
'''
fullpath = self.myDump + prefix + self.baseFilename
with open(fullpath, 'w') as h:
h.writelines(someList)
h.close()
print "saved %s" % fullpath
return fullpath
I'd like to be able to add a variable to each function that specifies the write method to use, e.g. (writeMethod=writelines). I considered just passing in a string and using one of the black-magic functions-- exec() I guess-- but that can't possibly be right since no one ever seems to use those functions. This whole example may be relatively silly, since I could just work around it, but I decided I'd benefit from knowing how to pass these sorts of instance-methods (is that the right term?). Is this related to binding and unbinding? All I need for a good answer is the syntax required to pass 'write,' 'writelines' etc. Could be simple as: writeMethod = insert_your_syntax_here. Would love additional explanation or guidance though. Thanks.
You can get a "bound method" from an object, which is then callable as a function without having a reference to the object.
f = obj.method
f(args)
# is equivalent to
obj.method(args)
However, that's not useful for you, as you create the object you want to use only in the method - you can't pass it in there as bound method. You can factor out the creation of fullpath
, although this only saves you half of the redundancy. One option, which I'd consider overkill, would be passing a callback which return the function to use for writing.
Another option would be a decorator to factor out all the common parts and push the rest into a callback, the decorated function:
def uses_file(prefix_default):
def decorator(f):
@functools.wraps(f)
def decorated(self, data, prefix=prefix_default):
fullpath = obj.myDump + prefix + obj.baseFilename
with open(fullpath, 'w') as h:
f(h, data, prefix)
print "saved", % fullpath
return fullpath
return decorated
return decorator
# ...
@uses_file(default_prefix="RESPONE_")
def writeFile(self, someHtml, prefix):
'''...'''
h.write(someHtml)
@uses_file(default_prefix="mechList_")
def writeList(self, someList, prefix):
'''...'''
h.writelines(someList)
There are different ways of doing this, for instance using lambdas:
def writeFile(self, someHtml, writeMethod=lambda f, data: f.write(data),
prefix="RESPONSE_"):
'''The calling functions will supply only the data to be written and
static prefixes, e.g. "full_" for the entire http-response.
'''
fullpath = self.myDump + prefix + self.baseFilename
with open(fullpath, 'w') as h:
writeMethod(h, someHtml)
h.close()
print "saved %s" % fullpath
return fullpath
精彩评论