Python: объект со списком объектов - создание методов на основе свойств членов списка - PullRequest
0 голосов
/ 21 ноября 2018

У меня есть класс, который содержит список примерно так:

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.Например, в среднем.Я мог бы написать следующее в 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(), особенно когда я добавляю больше свойств, также кажется неразумным.

Я понимаю, что пытаюсь инкапсулировать однострочник в этом примере, ноЕсть ли лучший способ создать такие методы, основанные на свойствах объектов в списке зоопарка?Я немного посмотрел на фабричные функции, поэтому, когда я их лучше понимаю, я думаю, что смогу написать что-то вроде этого:

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 рассказывали моему зоопарку, какие свойства можно превратить в методы get_average().Переходя к самому концу, скажем, я подкласс Animal и хотел бы указать, что он создает новый метод усреднения: (ниже приведен псевдокод, я не знаю, могут ли декораторы использоваться таким образом)

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

Затем, после добавления тигра в зоопарк, мой метод добавления животных в объект зоопарка будет знать, как создать метод get_average_tail_length() для этого экземпляра зоопарка.Вместо того, чтобы вести список того, какие средние методы мне нужно сделать, объекты типа Animal указывают, какие вещи можно усреднить.

Есть ли хороший способ получить такой метод генерации?Или, кроме getattr(), существует другой подход, чтобы сказать: "выполнить некоторые вычисления / работать с определенным свойством каждого члена в этом списке"?

1 Ответ

0 голосов
/ 21 ноября 2018

Попробуйте это:

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
...