Добавить атрибут в список классов, чтобы возвращать все объекты с определенным атрибутом

#python #list #class #filter #attributes

#python #Список #класс #Фильтр #атрибуты

Вопрос:

У меня следующая проблема:

Давайте установим сцену!

Допустим, у меня есть класс Person с несколькими основными атрибутами:

 class Person(object):
    def __init__(self, name=None, gender=None, single=None):
        self.name=name
        self.gender=gender
        self.single=single
  

И я создаю класс списка с именем Dating, который будет содержать все объекты Person

 class Dating(object):
    def __init__(self):
        self.members=[]

My_People=Dating()

My_People.members.append(Person("Jack","Male",False))
My_People.members.append(Person("Jill","Female",True))
My_People.members.append(Person("George","Male",True))
My_People.members.append(Person("Sandy","Female",False))
  

Правильно, так в чем проблема?

Можно ли присвоить атрибут классу list для доступа к отдельным элементам списка My_People, создав какой-то атрибут, например:

 My_People.members.singles
  

Так что это вернет список объектов Person, которые имеют один атрибут == True?

Спасибо за всю помощь. (кстати, у меня очень мало предыдущего опыта работы с Python)

Комментарии:

1. прекратите вызывать __init__ метод, он работает таким образом без вашей помощи)

2. Отредактировал вопрос. Теперь лучше?

3. Кто-нибудь знает, почему это было отклонено? Обратная связь полезна, поэтому я могу отредактировать вопрос и, надеюсь, решить проблему

Ответ №1:

Чтобы добавить подобные атрибуты, вам нужно будет подкласс типа списка:

 class FilterableList(list):
    def __getattr__(self, name):
        # assume non-existing attributes are boolean filters
        return [elem for elem in self if getattr(elem, name)]
  

Обратите внимание, что это не так гибко; True таким образом находят только значения. Вы можете придать именам атрибутов больше смысла, но обычно вы все равно захотите сделать это в Dating классе и использовать методы для более выразительной фильтрации ваших данных.

ДЕМОНСТРАЦИЯ:

 >>> class Person(object):
...     def __init__(self, name, gender, single):
...         self.name=name
...         self.gender=gender
...         self.single=single
...     def __repr__(self):
...         return 'Person({name!r}, {gender!r}, {single!r})'.format(**vars(self))
... 
>>> class FilterableList(list):
...     def __getattr__(self, name):
...         # assume non-existing attributes are boolean filters
...         return [elem for elem in self if getattr(elem, name)]
... 
>>> members = FilterableList([Person("Jack","Male",False), Person("Jill","Female",True), Person("George","Male",True), Person("Sandy","Female",False)])
>>> members
[Person('Jack', 'Male', False), Person('Jill', 'Female', True), Person('George', 'Male', True), Person('Sandy', 'Female', False)]
>>> members.single
[Person('Jill', 'Female', True), Person('George', 'Male', True)]
  

Комментарии:

1. Спасибо за это, это именно то, что мне нужно, и мне нравится гибкость вашего решения. Кроме того, я никогда раньше не использовал repr , и это действительно пригодится!

Ответ №2:

Прежде всего, вам не нужно вызывать __init__() My_People экземпляр, Python делает это автоматически.

Для вопроса вы можете создать подкласс из list и добавить singles свойство, подобное этому:

 class Person(object):
    def __init__(self, name=None, gender=None, single=None):
        self.name=name
        self.gender=gender
        self.single=single

class Dating(list):
    @property
    def singles(self):
        return [person for person in self if person.single ]

My_People=Dating()

My_People.append(Person("Jack","Male",False))
My_People.append(Person("Jill","Female",True))
My_People.append(Person("George","Male",True))
My_People.append(Person("Sandy","Female",False))

print My_People.singles
  

Комментарии:

1. отлично, я это понимаю. Поэтому вам нужно перебирать список My_People каждый раз, когда вы хотите получить доступ к My_People.singles, и нет прямого способа вызвать эти объекты с помощью атрибута single==True .

2. Мне нравится предложение создать подкласс из списка, очень удобно

3. @user1083734 Вы, конечно, можете сохранить .singles возвращаемые результаты, чтобы избежать повторения списка при каждом доступе.

4. но тогда мне не пришлось бы обновлять результаты синглов, вызывая функцию снова каждый раз, когда изменяется список My_People?

Ответ №3:

Как насчет этого:

 class Attribute(list):
    def __getattr__(self, attr):
        if attr == 'single':
            return [person for person in self if person.single]
        raise AttributeError()

class Person(object):
    def __init__(self, name=None, gender=None, single=None):
        self.name = name
        self.gender = gender
        self.single = single

    def __repr__(self):
        return 'Person({person.name}, {person.gender}, {person.single})'.format(person=self)


class Dating(object):
    members = Attribute()


My_People = Dating()

My_People.members.append(Person("Jack", "Male", False))
My_People.members.append(Person("Jill", "Female", True))
My_People.members.append(Person("George", "Male", True))
My_People.members.append(Person("Sandy", "Female", False))

>>> print My_People.members.single
[Person(Jill, Female, True), Person(George, Male, True)]
>>> print My_People.members
[Person(Jack, Male, False), Person(Jill, Female, True), Person(George, Male, True), Person(Sandy, Female, False)]
  

Ответ №4:

Если вы не привязаны к My_People.members.singles синтаксису, вы могли бы сделать это намного проще:

 class Dating(object):

    def __init__(self):
        self.members = []

    @property
    def single_members(self):
        return [m for m in members if m.single]
  

Теперь вы получаете доступ к списку с помощью:

 My_People.single_members