编写接受Python中的单个项或可迭代的方法

时间:2012-09-26 14:07:02

标签: python

我有设计问题。我想编写一个方法接受单个对象或可迭代的对象。例如,假设我有类Dog:

class Dog(object):
    """
    An animal with four legs that may slobber a lot
    """
    def __init__(self, name="Fido"):
       self.name = name

现在让我们说我有一个使用Dog类的类,比如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

在上面的例子中,我实现了两种方法walk和water。水方法更加pythonic,因为它使用鸭子打字。但是,我想进一步谈谈。假设我有一个可以为不同类型的动物浇水的护理员课程:

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

现在,我发现的一件事是,每件事物都可以知道如何给自己浇水,然后护理人员只需要称之为水的方法。例如:

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

使用代码的方式可能如下:

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)

我还有其他一些想法,但在采取特定的设计方法之前,我想从其他可能遇到过此类问题的人那里获得一些意见。如果你也可以列出你的想法的利弊。这将是一个奖金!感谢。

更新:到目前为止,似乎有两个同样好的建议。

  1. 测试传入的参数的行为,例如,写一个 is_itereable方法。
  2. 使用位置参数并迭代位置参数列表中的项目列表。
  3. 我还没有确定哪个更适合我的实际问题。

2 个答案:

答案 0 :(得分:5)

您可以使用*args“通配符”位置参数:

def walk(self, *dogs):
    for dog in dogs:
        # handle each dog.

并将其称为:

owner.walk(onedog, anotherdog)

owner.walk(*listofdogs)

答案 1 :(得分:0)

我建议你把两者结合起来。在某种程度上,狗,猫和鸟可以使用相同的方法,使用方法名称而不关心您实际拥有的对象。 (常用的方法是你的动物API)。但是,如果你传递一个容器,这将不起作用,除非你去伪造一个容器类,将每个API方法应用于每个子容器。这在技术上是可行的,但我发现以下内容更具吸引力:编写代码以始终期望容器,并通过将它们嵌入到列表中来转换单个对象:

class Caretaker(object):
    ...
    def water(self, things):
        if isinstance(things, animal): # actually only one: fix it
            things = [ things ]

        for thing in things:
            ...

这假设您有一个父类animal,您从中导出dogbird。或者,您可以明确检查things是否可迭代,如下所示:

        if not isinstance(things, collections.Iterable):
            things = [ things ]

但我更喜欢第一种方法。