Python:带有对象列表的对象-基于列表成员的属性创建方法

时间:2018-11-21 00:37:50

标签: python python-3.x list object attributes

我有一个包含如下列表的类:

class Zoo:
    def __init__(self):
        self._animals = []

我用具有各种属性的动物对象填充动物列表:

class Animal:
    def __init__(self, speed, height, length):
        self._speed = speed
        self._height = height
        self._length = length

您可以想象Animal具有其他属性的子类。我希望能够编写执行相同计算但对Animal的不同属性执行的方法。例如,平均值。我可以在Zoo中编写以下内容:

def get_average(self, propertyname):
    return sum(getattr(x, propertyname) for x in self.animals) / len(self.animals)

该字符串查找不仅使我无法很好地记录文档,而且使用getattr似乎很奇怪(也许我只是紧张地传递字符串?)。如果这是一种好的标准做法,那很好。创建get_average_speed()get_average_height()get_average_length()方法(尤其是在我添加更多属性时)似乎也不明智。

我意识到我想在此示例中封装单线,但是是否有更好的方法去基于Zoo列表中对象的属性来创建这样的方法?我稍微看了一些工厂功能,所以当我对它们的理解更好时,我想我可以写这样的东西:

all_properties = ['speed', 'height', 'length']
for p in all_properties:
    Zoo.make_average_function(p)

然后,任何Zoo实例都将具有名为get_average_speed()get_average_height()get_average_length()的方法,最好使用漂亮的文档字符串。更进一步,我真的很喜欢Animal对象本身告诉我的Zoo哪些属性可以转换为get_average()方法。到最后,假设我将Animal子类化,并希望它表明它创建了一个新的average方法:(以下是伪代码,我不知道是否可以这样使用装饰器)

class Tiger(Animal):
    def __init__(self, tail_length):
        self._tail_length = tail_length

    @Zoo.make_average_function
    @property
    def tail_length(self):
        return self._tail_length

然后,在将Tiger添加到Zoo时,我将动物添加到Zoo对象的方法将知道为Zoo实例创建get_average_tail_length()方法。动物型对象不必保留我需要制作的平均方法的列表,而可以表示可以平均的东西。

是否有一种很好的方法来生成这种方法?还是除了getattr()之外还有另一种说法是“对列表中每个成员的特定属性进行一些计算/工作”?

1 个答案:

答案 0 :(得分:1)

尝试一下:

import functools
class Zoo:
    def __init__(self):
        self._animals = []

    @classmethod
    def make_average_function(cls, func):
        setattr(cls, "get_average_{}".format(func.__name__), functools.partialmethod(cls.get_average, propertyname=func.__name__))
        return func

    def get_average(self, propertyname):
        return sum(getattr(x, propertyname) for x in self._animals) / len(self._animals)


class Animal:
    def __init__(self, speed, height, length):
        self._speed = speed
        self._height = height
        self._length = length


class Tiger(Animal):
    def __init__(self, tail_length):
        self._tail_length = tail_length

    @property
    @Zoo.make_average_function
    def tail_length(self):
        return self._tail_length


my_zoo = Zoo()
my_zoo._animals.append(Tiger(10))
my_zoo._animals.append(Tiger(1))
my_zoo._animals.append(Tiger(13))
print(my_zoo.get_average_tail_length())

注意:如果不同的动物园里有不同类型的动物,则会导致混乱。

示例

class Bird(Animal):
    def __init__(self, speed):
        self._speed = speed

    @property
    @Zoo.make_average_function
    def speed(self):
        return self._speed

my_zoo2 = Zoo()
my_zoo2._animals.append(Bird(13))
print(my_zoo2.get_average_speed())   # ok
print(my_zoo.get_average_speed()) # wrong
print(my_zoo2.get_average_tail_length()) # wrong