2015-02-20 2 views
4

Я не знаю, что то, что я пытаюсь сделать, настолько не-Pythonic, что я просто пытаюсь сделать это неправильно, или если я не знаю, как спросить вопрос правильно. Для меня имеет смысл это сделать, но я искал 15 разных способов и не могу найти ответ.Доступ к списку объектов Python по атрибуту объекта

Что я хочу сделать, кажется таким простым: у меня есть список объектов. Я хочу получить доступ к этому списку с помощью свойства объектов. Этот код работает:

class Fruit: 
    def __init__(self, name, color): 
     self.name = name 
     self.color = color 

    def __str__(self): 
     return self.name  

class BaseballPlayer: 
    def __init__(self, name, number, position): 
     self.name = name 
     self.number = number 
     self.position = position 

    def __str__(self): 
     return self.name  

class ListByProperty(list): 
    def __init__(self, property, list): 
     super(ListByProperty, self).__init__(list) 
     self.property = property 

    def __getitem__(self, key): 
     return [item for item in self if getattr(item, self.property) == key][0] 

fruits = ListByProperty('name', [Fruit('apple', 'red'), Fruit('banana', 'yellow')]) 

baseballTeam = ListByProperty('position', [BaseballPlayer('Greg Maddux', 31, 'P'), 
              BaseballPlayer('Javy Lopez', 8, 'C')]) 
teamByNumber = ListByProperty('number', baseballTeam) 

print 'Apples are', fruits['apple'].color 

pitcher = baseballTeam['P'] 
print 'The pitcher is #%s, %s' % (pitcher.number, pitcher) 
print '#8 is', teamByNumber[8] 

>>> Apples are red 
The pitcher is #31, Greg Maddux 
#8 is Javy Lopez 

Но действительно ли мне нужно, чтобы мой собственный класс списка сделал что-то такое простое? Нет ли общего способа, кроме цикла или списка? Кажется, что это должно быть очень распространенным явлением, чтобы иметь список объектов и доступ к элементам в списке по свойствам объектов. Похоже, он должен быть обычно поддержан способом, подобным sorted(key=...).

Обратите внимание, что это не тот случай, когда требуется dict. В самом деле, весь смысл использования списка объектов вместо Dict, чтобы избежать того, чтобы сделать что-то вроде:

fruits = {'apple': Fruit('apple', 'red')} 

... которая требует от вас ввести apple дважды. Похоже, что там должен быть универсальный способ сделать что-то вроде этого:

print 'Apples are', fruits['apple'].color 

... без подклассы list.

И хорошо, вы можете построить Dict так:

fruits = [Fruit('apple', 'red'), Fruit('banana', 'yellow')] 
fruits = {f.name: f for f in fruits} 

Или вы можете однострочный это, но до сих пор кажется ... э-э ... синтаксически кисло? :)

Лучший способом я понял, до сих пор является:

class DictByProperty(dict): 
    def __init__(self, property, list): 
     super(DictByProperty, self).__init__({getattr(i, property): i for i in list}) 
     self.property = property 

fruits = DictByProperty('name', [Fruit('apple', 'red')]) 

Ну что ж, спасибо, я узнал много уже от этого вопроса.

+1

Помимо объяснения: '{Foo, Bar, Baz} 'является * set *,' {'foo': Foo, 'bar': Bar} 'is * dict *. Обратите внимание, что у одного есть ключи, а другой нет. – deceze

+0

Глупый я, я забыл, что '{}' также создает множества. :) – blujay

ответ

4
class Fruit: 
    def __init__(self, name, color): 
     self.name = name 
     self.color = color 

fruits = dict(zip(['apple', 'banana'], [Fruit('apple', 'red'), Fruit('banana', 'yellow')])) 

print("An apple is %s" % fruits['apple'].color) 

ИЛИ:

fruits = {fruit.name : fruit for fruit in [Fruit('apple', 'red'), Fruit('banana', 'yellow')]} 

print("An apple is %s" % fruits['apple'].color) 

Следующая делает Infact производить набор:

fruits = {Fruit('apple', 'red'), Fruit('banana', 'yellow')} 

Обратите внимание на отличие от того, как я создал Dict

fruits = [Fruit('apple', 'red'), Fruit('banana', 'yellow')] 

print("An apple is %s" % fruits[fruits.index('apple')].color) 

не работает, потому что ваш список содержит объекты типа фруктов не строк, и это та же самая история здесь:

fruits = FruitList([Fruit('apple', 'red'), Fruit('banana', 'yellow')]) 

print("An apple is %s" % fruits['apple'].color) 

Для того, чтобы выше метод работы, выполните следующие действия:

class Fruit: 
    def __init__(self, name, color): 
     self.name = name 
     self.color = color 

    def __eq__(self, other): 
     if isinstance(other, Fruit): 
      return (self.name, self.color) == (other.name, other.color) 
     return self.name == other 

fruits = [Fruit('apple', 'red'), Fruit('banana', 'yellow')] 
print("An apple is %s" % fruits[fruits.index('apple')].color) 

Я не рекомендую это, потому что в случае, когда ваш список содержит строку apple, тогда вызов атрибута color становится недействительным, поскольку строки не имеют атрибута color

+0

«Не работает, потому что в вашем списке содержатся объекты типа фрукты, а не строки ...» Но я установил '__str __()' на объект «Fruit». Мой вопрос в том, почему метод 'index' там не работал. Благодарю. – blujay

+0

Метод '__str__' вызывается только при печати объекта. Он не используется для сравнения. Если бы вы перегрузили метод '__eq__', это могло бы сработать. См. Мое редактирование – smac89

+0

А, '__eq__'. Я не знаю, почему я пропустил это, что используется для списков. Я не понял и подумал, что он используется в dicts. Благодарю. По-прежнему кажется, что единственный способ заставить 'fruit ['apple']' работать в подклассе 'list'. Это действительно единственный способ? – blujay

0

Вы можете создать экземпляр.

apple = Fruit('apple', 'red') 
print(apple.color) # I use Python 3.x 

Я не уверен, что следую вашему вопросу. Но, возможно, это помогает.

редактировать: в попытке получить мою репутацию назад ...

можно использовать экземпляры ТЕЧЕНИЕ словаря. Например ...

banana = Fruit('banana', 'yellow') 
apple = Fruit('apple', 'red') 
fruits = {'apple':apple, 'banana':banana} 

или, если вы предпочитаете:

fruits = {'apple':Fruit('apple', 'red'), 'banana':Fruit('banana', 'yellow')} 

в любом случае, вы можете назвать цвет фруктовой просто с

print(fruits['banana'].color) 
+0

Довольно круто, что стек обмен знает цвет класса Fruit! – user14241

+0

Я не понимаю, почему это проголосовало. Что не так с этим методом? – user14241

+0

Я переписал свой вопрос, чтобы уточнить, что я ищу, чтобы избежать необходимости вводить имя плода дважды. Благодарю. :) – blujay

Смежные вопросы