class PossibleMissingAttribute(object):
def __init__(self, object, name):
self._object = object
self._name = name
def __call__(self, *args, **kwargs):
return self._object._method_missing(self._name, *args, **kwargs)
def __str__(self):
return self.__class__.__name__ + ": " + str(self._object) + "." + self._name
def __getattr__(self, name):
return None
def __nonzero__(self):
return False
class MethodMissingError(Exception):
def __init__(self, object, name, *args, **kwargs):
self.object = object
self.name = name
self.args = args
self.kwargs = kwargs
def __str__(self):
return repr(str(self.object) + "." + self.name)
class Missing(object):
def __getattr__(self, name):
return PossibleMissingAttribute(self, name)
def _method_missing(self, name, *args, **kwargs):
raise MethodMissingError(self, name, *args, **kwargs)
Three objects and that's it! One to be a placeholder when an argument is missing. One object to represent the MethodMissingError and the last to be an abstract class to inherit from. But, you could easily just put these methods anywhere. Here's my tests:
import unittest
class TestMissing(unittest.TestCase):
def test_missing(self):
class TestClass(Missing):
def __str__(self):
return self.__class__.__name__
def existing(self):
return "i am"
def _method_missing(self, name, *args, **kwargs):
return args[0]
self.assertEquals("am not", TestClass().missing("am not"))
self.assertEquals("i am", TestClass().existing())
self.assertEquals(None, TestClass().missing.something_else_missing)
self.assertFalse(TestClass().missing)
self.assertEquals("PossibleMissingAttribute: TestClass.missing", str(TestClass().missing))
def test_exception(self):
class TestClass(Missing):
def __str__(self):
return self.__class__.__name__
self.failUnlessRaises(MethodMissingError, lambda: TestClass().missing())
You've seen my implementation. Here's another one that I found on the net: method_missing in Python
class MethodMissing(object):
def method_missing(self, attr, *args, **kwargs):
“”" Stub: override this function “”"
raise AttributeError(”Missing method %s called.”%attr)
def __getattr__(self, attr):
def callable(*args, **kwargs):
return self.method_missing(attr, *args, **kwargs)
return callable
Um, this version is much shorter than mine. It gets the same job done and is more obvious of what it is doing. Simply returning a function on __getattr__ is the best way to go. It's what my PossibleArgumentMissing was doing. But, where I tried to get all fancy using __call__ to mimic a function which caused my version to be longer. I was also trying to get the argument to come back to return false in if conditions and be almost like None (again, I'm learning and was looking at what was possible). Simplest is best.
This was a fun thought exercise. I love all the hooks Python provides to allow you to get underneath the hood if need be. I will be soon doing a post on descriptors and even decorators. I find Python tends to sway heavier on the functional side of programming versus object-oriented. I'm loving the succinct, yet readable code. Too many languages get into the being so terse that they sacrifice readability or debugability (is that a word? Is now!).
No comments:
Post a Comment