Reputation: 7052
Suppose we have the following code. I want to swap out the inner parse_place
function. How do you do this without replacing the whole method?
class GoogleV3Place(GoogleV3):
"""Simply extends the GoogleV3 to bucket the object into a place"""
def parse_json(self, page, exactly_one=True):
"""Returns location, (latitude, longitude) from json feed."""
if not isinstance(page, basestring):
page = util.decode_page(page)
self.doc = json.loads(page)
places = self.doc.get('results', [])
if not places:
check_status(self.doc.get('status'))
return None
elif exactly_one and len(places) != 1:
raise ValueError(
"Didn't find exactly one placemark! (Found %d)" % len(places))
def parse_place(place):
"""This returns an object how we want it returned."""
location.formatted_address = place.get('formatted_address')
location.latitude = place['geometry']['location']['lat']
location.longitude = place['geometry']['location']['lng']
latitude = place['geometry']['location']['lat']
longitude = place['geometry']['location']['lng']
return (location, (latitude, longitude))
if exactly_one:
return parse_place(places[0])
else:
return [parse_place(place) for place in places]
Upvotes: 4
Views: 1392
Reputation: 2126
The proper way to do this is at compile time. You need to make an abstract base class (the ABC) with the abc module. This will not allow the client code to instantiate the class unless the virtual functions are implemented whic is caught at compile time. Good for polymorphic programming. The virtual functions are the ones that have the @abstractmethod decorator. You can define real functions in the ABS too.
import abc
class MyABC(object):
__metaclass__ ABCMeta
@abstractmethod
def my_virtual_function(self):
pass
def real_function(self):
print "ABClass function"
The derived class now inherits the ABC and implements the virtual functions.
class MyDerived(MyABC):
def my_virtual_function(self):
print "ok"
You must have python 2.6 or later for this.
For more on the topic... http://docs.python.org/library/abc.html
if you are stuck with python 2.4 then you have to contend with a much less desirable situation. You must have a base class that throws a runtime exception.
class MyBaseClass(object):
def my_redefinable(self):
raise NotImplementedError("You must implement my_redefinable() ")
class MyDerivedClass(MyBaseClass):
def __init__(self):
pass
def my_redefinable(self):
print "ok"
test = MyBaseClass()
test.my_redifinable() # throws error
test = MyDerivedclass()
test my_redefinable() # is ok
Upvotes: 1
Reputation: 182
You can not access nested functions, these are only visible inside the function, like local variables. However, you may put parse_place into separate method and then override with regular inheritance.
Upvotes: 0
Reputation: 18750
If you have control over the GoogleV3Place code (ie it's code you've written and isn't in a library), then I'd refactor parse_json to take a "parse_place_fn" argument, and move parse_place to be a top-level function (if accessibility is an issue you can always prefix it with double-underscore):
def parse_place(place):
"""This returns an object how we want it returned."""
location.formatted_address = place.get('formatted_address')
location.latitude = place['geometry']['location']['lat']
location.longitude = place['geometry']['location']['lng']
latitude = place['geometry']['location']['lat']
longitude = place['geometry']['location']['lng']
return (location, (latitude, longitude))
class GoogleV3Place(GoogleV3):
"""Simply extends the GoogleV3 to bucket the object into a place"""
def parse_json(self, page, exactly_one=True, parse_place_fn = parse_place):
"""Returns location, (latitude, longitude) from json feed."""
if not isinstance(page, basestring):
page = util.decode_page(page)
self.doc = json.loads(page)
places = self.doc.get('results', [])
if not places:
check_status(self.doc.get('status'))
return None
elif exactly_one and len(places) != 1:
raise ValueError(
"Didn't find exactly one placemark! (Found %d)" % len(places))
if exactly_one:
return parse_place_fn(places[0])
else:
return [parse_place_fn(place) for place in places]
Now any function you create which takes a place and returns a tuple of the form (location, (latitude, latitude)) can be passed to parse_json, and if no function is specified it uses the default of parse_place.
Upvotes: 3
Reputation: 16338
If you really want to make some method polymorphic, you can use callable objects instead of functions. You could delegate call to private class-level callable or implement binding to instances in that callable objects.
But in that concrete example there is no reasons to embed parse_place
into parse_json
Upvotes: 0