I'd be interested in seeing an example of meta-programming in Python as nothing really seems to fit in C#. There's generics, delegates, reflection as above and meta-data in the form of attribute decoration as for the ProviderType property, but nothing that really does what I think you're suggesting.
I must say, I barely understood that code at all. If you've got the time, do you think you could explain what's going on there? (I don't know the language at all, and that
abstract class business confuses me.)
I wasn't suggesting anything in particular by metaprogramming: it seems to cover a huge range of things. I generally understand it as referring to programs altering themselves or writing other programs.
Something I do often in Python is to cache the results of long-running functions:
from urllib import quote_plus, urlopen
class memoize(object):
def __init__(self, func):
self.func = func
self._cache = {}
def __call__(self, *args):
if not self._cache.has_key(args):
self._cache[args] = self.func(*args)
return self._cache[args]
@memoize
def google(query):
url = "http://www.google.com/search?q=%s" % quote_plus(query)
return urlopen(url).read()
if __name__ == "__main__":
import time
for x in range(10):
start = time.time()
google('some search')
print "%d: %04f secs" % (x + 1, time.time() - start)
Output:1: 1.399915 secs
2: 0.000024 secs
3: 0.000006 secs
4: 0.000006 secs
5: 0.000006 secs
6: 0.000006 secs
7: 0.000006 secs
8: 0.000005 secs
9: 0.000005 secs
10: 0.000006 secs
@memoize is a Python decorator that wraps the following function. So, whenever
google() is called, in fact
memoize(google).__call__() is called. If it hasn't seen the exact arguments already, it calls
google() with the args and caches the result before returning it, otherwise it simply returns the cached result.
It's a contrived example, but nevertheless, the first call of
google('some search') takes about 1.7 seconds on my machine and subsequent calls of the same take 0.000006 seconds or less.
I don't consider that particularly 'meta' as it's not really dynamic, just a time-saver. The most complex stuff I like to touch would be something like this:
import new
class Data:
def __init__(self):
self.attr1 = "foo"
self.attr2 = "bar"
def dumpxml(self):
s = "<item>\n"
for k, v in self.__dict__.items():
if k == 'dumps': continue
s += "\t<attribute name='%s' value='%s' />\n" % (k, v)
s += "</item>"
return s
def dumpjson(self):
d = self.__dict__.copy()
del(d['dumps'])
return repr(d)
class CockstashException(Exception):
pass
class DataProvider(object):
def __init__(self, format="xml"):
self.format = format
def get(self):
d = Data()
if self.format == 'xml':
func = new.instancemethod(dumpxml, d, d.__class__)
d.dumps = func
elif self.format == 'json':
func = new.instancemethod(dumpjson, d, d.__class__)
d.dumps = func
else:
raise CockstashException('Invalid format, cockstash.')
return d
if __name__ == '__main__':
xmlprovider = DataProvider('xml')
jsonprovider = DataProvider('json')
bollixprovider = DataProvider('MS Word')
xml = xmlprovider.get()
print xml.dumps()
json = jsonprovider.get()
print json.dumps()
bollix = bollixprovider.get() # throws an exception
Output:<item>
<attribute name='attr2' value='bar' />
<attribute name='attr1' value='foo' />
</item>
{'attr2': 'bar', 'attr1': 'foo'}
Traceback (most recent call last):
File "meta.py", line 51, in <module>
bollix = bollixprovider.get() # throws an exception
File "meta.py", line 39, in get
raise CockstashException('Invalid format, cockstash.')
__main__.CockstashException: Invalid format, cockstash.
This code adds the instance method
dumps to dump the object's attributes to a format specified at runtime. As far as I know, that would be nigh-on impossible in a statically-compiled language as
self in the
dumpxml and
dumpjson functions-cum-methods is not bound until runtime. (I may be completely wrong here.)
Before anyone jumps down my throat, I know it's hackish, and I know that
repr() doesn't return valid JSON data, but rather Python source code for the object (at least in the case of Python primitives), however, in the context of this example, it's also valid JSON data.
I try to avoid anything more complex than that, as it invariably confuses the fuck out of me the next time I read my own code.
BTW: You can run either of the examples by copying the code to a text file and running it with Python.