【问题标题】:Writing methods that accept either a single item or an iterable in Python在 Python 中编写接受单个项目或可迭代项目的方法
【发布时间】:2012-09-18 04:32:33
【问题描述】:

我有一个设计问题。我想编写一个方法接受单个对象或对象的可迭代。例如,假设我有 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. 使用位置参数并遍历位置参数列表中的项目列表。

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

【问题讨论】:

标签: python


【解决方案1】:

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

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

并将其称为:

owner.walk(onedog, anotherdog)

owner.walk(*listofdogs)

【讨论】:

  • 谢谢 Martjin,我只是在查看位置参数。这可能是要走的路。
  • 这很好,因为它将所有内容都转换为零行代码的列表。不适用于此示例的限制是 dogs 必须是最后一个参数;并且您不能将可能永远运行的迭代器传递给它(因为它需要在调用 walk() 之前完全展开)。
  • 从技术上讲,这似乎是最 Pythonic 的,但我担心无法传入生成器。当然,您可以使用 list(args),但只有在 args 可以放入内存时才有效。
【解决方案2】:

我建议你将两者结合起来。如果狗、猫和鸟可以拥有相同的方法,请使用方法名称而不关心您实际拥有哪个对象。 (常用方法是你的动物 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 ]

但我更喜欢第一种方法。

【讨论】:

    猜你喜欢
    • 2015-04-30
    • 1970-01-01
    • 2020-06-11
    • 2017-03-05
    • 2011-02-11
    • 2012-07-06
    • 2022-08-24
    • 2021-03-07
    • 2012-01-02
    相关资源
    最近更新 更多