0

我有一个设计问题。我想编写一个方法接受单个对象或对象的可迭代。例如,假设我有 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。water 方法更 Pythonic,因为它使用 Duck Typing。但是,我想进一步讨论。假设我有一个可以给不同类型的动物浇水的看护班:

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. 使用位置参数并遍历位置参数列表中的项目列表。

我还没有确定哪个更适合我的实际问题。

4

2 回答 2

6

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

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

并将其称为:

owner.walk(onedog, anotherdog)

或者

owner.walk(*listofdogs)
于 2012-09-26T14:10:04.273 回答
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 ]

但我更喜欢第一种方法。

于 2012-09-26T14:44:45.807 回答