我有设计问题。我想编写一个方法接受单个对象或可迭代的对象。例如,假设我有类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)
我还有其他一些想法,但在采取特定的设计方法之前,我想从其他可能遇到过此类问题的人那里获得一些意见。如果你也可以列出你的想法的利弊。这将是一个奖金!感谢。
更新:到目前为止,似乎有两个同样好的建议。
我还没有确定哪个更适合我的实际问题。
答案 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
,您从中导出dog
和bird
。或者,您可以明确检查things
是否可迭代,如下所示:
if not isinstance(things, collections.Iterable):
things = [ things ]
但我更喜欢第一种方法。