I have a design issue. I would like to write a method accepts either a single object or an iterable of objects. For example, lets say I have the class Dog:
class Dog(object):
"""
An animal with four legs that may slobber a lot
"""
def __init__(self, name="Fido"):
self.name = name
Now let's say that I have a class that uses the Dog class, say DogWalker:
class DogWalker(object):
"""
Someone who cares for Fido when Human Companion is not available
"""
def __init__(self):
self.dogs_walked = 0
self.dogs_watered = 0
def walk(self, dogs):
"""
Take a dog for a walk
"""
# This is not Pythonic
if isinstance(Dog, dogs):
# Walk a single dog
pass
self.dogs_walked +=1
else:
# walk lots of dogs
pass
def water(self, dogs):
"""
Provide water to dogs
"""
# More pythonic
try:
# Try to water single dog
...
self.dogs_walked += 1
except AttributeError:
# Try to water lots of dogs
pass
In the example, above I've implemented two methods walk and water. The water method is more pythonic in that it uses Duck Typing. However, I'd like to take the conversation a bit further. Let's say I have a care taker class that can water different types of animals:
class CareTaker(object):
"""
Someone who cares for things
"""
def __init__(self):
self.things_watered = 0
def water(self, things):
"""
Provide water to a thing
"""
# Here is where I need help!
# 1. I need to figure out if things is a single item or iterable
# 2. If thing is a single item I need to figure out what type of thing a thing is
# 3. If thing is an iterable, I need to figure out what type of things are in the iterable.
pass
Now, one thing that occurred to me is that each thing could know how to water itself, and then the care taker would only need to call the things water method. For example:
class CareTaker(object):
"""
Someone who cares for things
"""
def __init__(self):
self.things_watered = 0
def water(self, thing):
"""
Provide water to a thing
"""
result = thing.water()
self.things_watered += 1
return result
That way using the code might look like:
ct = CareTaker()
dog = Dog()
bird = Bird()
water_dog = ct.water(dog)
water_bird = ct.water(bird)
things = [dog, bird]
for thing in things:
ct.water(thing)
I have a few other ideas, but I would like to get some input from others who might have faced such an issue, before I take a specific design approach. If you could list pros and cons of your ideas as well. That would be a bonus! Thanks.
UPDATE: There appear to be two equally good suggestions so far.
- Test the behavior of the arguments passed in, for example, write an is_itereable method.
- Use positional arguments and iterate over the list of items in the positional argument list.
I have yet to determine which is better for my actual problem.