从列表中查找属性等于某个值的对象,并在之后获取下一个对象
Find object from list that has attribute equal to some value and also get the next object after
我能够找到一个对象,其属性等于某个值。但我还想从列表中获取该对象之后的对象(如果找到的对象是列表中的最后一个对象,则该对象之后的下一个对象应该是第一个对象)。类似于:
from pprint import pprint
class User(object):
def __init__(self, name):
self.name = name
users = []
users.append(User("Peter"))
users.append(User("James"))
users.append(User("John"))
# find object that has attribute name equal to James
pprint(vars([user for user in users if user.name == "James"][0]))
并且 pprint 行的输出打印:
{'name': 'James'}
没错。
我想问你如何获取 "James"
之后的下一个对象,以及如果我要搜索 "John"
应该返回 "John"
之后的下一个对象 "Peter"
.建议?
我也试过 itertools
,但如果找到的元素是最后一个,我就无法获取下一个元素:
from itertools import enumerate
_i = next(i for i, user in enumerate(users) if (user.name == "John"))
print users[_i + 1] #this is not working
我可以在操作前添加 if 条件来更改计数器 [_i+1]
但我想知道是否有更流畅的解决方案?
要处理最后一个元素,可以使用取模:index % len(users)
.
这是一种方法:
def find_after_name(users, name):
for i, user in enumerate(users):
if user.name == name:
return users[(i+1) % len(users)]
另一种选择是 zip
带有列表移位副本的列表。 deque.rotate()
对这种移位很有用:
from collections import deque
def find_after_name(users, name):
users2 = deque(users)
users2.rotate(-1)
for user1, user2 in zip(users1, users2):
if user1.name == name:
return user2
既然选择了使用 OOP,为什么不实现一个继承自内置列表 class 的 UserList class?
class User(object):
def __init__(self, name):
self.name = name
def __str__(self):
return repr(self)
def __repr__(self):
return "User('{0}')".format(self.name)
class UserList(list):
def find(self, name):
for k, user in enumerate(self):
if user.name == name:
return k, user
def next_to(self, name):
"""get user next to the one of name (e.g. 'James')"""
index, user = self.find(name)
next_to_index = self.get_next_to_index(index)
return self[next_to_index]
def get_next_to_index(self, index):
next_to_index = index + 1
if next_to_index == len(self):
# meaning index is already the last element, need to reset index
next_to_index = 0
return next_to_index
users = UserList()
users.append(User("Peter"))
users.append(User("James"))
users.append(User("John"))
print users.find('James')
print users.next_to('James')
print users.next_to('John')
输出:
(1, User('James'))
User('John')
User('Peter')
为了好玩,我想看看有没有 itertools 的解决方案:
from itertools import dropwhile
def find_after_name(users, name):
for i, _ in dropwhile(lambda eu: eu[1].name != name, enumerate(users)):
return users[(i+1) % len(users)]
注意:确实应该有一个 list.index(value, key=None) 方法,其中 key 就像 list.sort() 的键参数。然后你可以这样做:
index = users.index("John", key=lambda u: u.name)
解决方案扩展列表class:
class extended_list(list):
def index(self, value, key=None):
if key is None:
return super().index(self, value)
try:
return next(i for i,o in enumerate(self) if value == key(o))
except StopIteration:
raise ValueError("{} is not in list".format(repr(value)))
def find_after_name(users, name):
i = extended_list(users).index(name, key=lambda u: u.name)
return users[(i+1) % len(users)]
我能够找到一个对象,其属性等于某个值。但我还想从列表中获取该对象之后的对象(如果找到的对象是列表中的最后一个对象,则该对象之后的下一个对象应该是第一个对象)。类似于:
from pprint import pprint
class User(object):
def __init__(self, name):
self.name = name
users = []
users.append(User("Peter"))
users.append(User("James"))
users.append(User("John"))
# find object that has attribute name equal to James
pprint(vars([user for user in users if user.name == "James"][0]))
并且 pprint 行的输出打印:
{'name': 'James'}
没错。
我想问你如何获取 "James"
之后的下一个对象,以及如果我要搜索 "John"
应该返回 "John"
之后的下一个对象 "Peter"
.建议?
我也试过 itertools
,但如果找到的元素是最后一个,我就无法获取下一个元素:
from itertools import enumerate
_i = next(i for i, user in enumerate(users) if (user.name == "John"))
print users[_i + 1] #this is not working
我可以在操作前添加 if 条件来更改计数器 [_i+1]
但我想知道是否有更流畅的解决方案?
要处理最后一个元素,可以使用取模:index % len(users)
.
这是一种方法:
def find_after_name(users, name):
for i, user in enumerate(users):
if user.name == name:
return users[(i+1) % len(users)]
另一种选择是 zip
带有列表移位副本的列表。 deque.rotate()
对这种移位很有用:
from collections import deque
def find_after_name(users, name):
users2 = deque(users)
users2.rotate(-1)
for user1, user2 in zip(users1, users2):
if user1.name == name:
return user2
既然选择了使用 OOP,为什么不实现一个继承自内置列表 class 的 UserList class?
class User(object):
def __init__(self, name):
self.name = name
def __str__(self):
return repr(self)
def __repr__(self):
return "User('{0}')".format(self.name)
class UserList(list):
def find(self, name):
for k, user in enumerate(self):
if user.name == name:
return k, user
def next_to(self, name):
"""get user next to the one of name (e.g. 'James')"""
index, user = self.find(name)
next_to_index = self.get_next_to_index(index)
return self[next_to_index]
def get_next_to_index(self, index):
next_to_index = index + 1
if next_to_index == len(self):
# meaning index is already the last element, need to reset index
next_to_index = 0
return next_to_index
users = UserList()
users.append(User("Peter"))
users.append(User("James"))
users.append(User("John"))
print users.find('James')
print users.next_to('James')
print users.next_to('John')
输出:
(1, User('James'))
User('John')
User('Peter')
为了好玩,我想看看有没有 itertools 的解决方案:
from itertools import dropwhile
def find_after_name(users, name):
for i, _ in dropwhile(lambda eu: eu[1].name != name, enumerate(users)):
return users[(i+1) % len(users)]
注意:确实应该有一个 list.index(value, key=None) 方法,其中 key 就像 list.sort() 的键参数。然后你可以这样做:
index = users.index("John", key=lambda u: u.name)
解决方案扩展列表class:
class extended_list(list):
def index(self, value, key=None):
if key is None:
return super().index(self, value)
try:
return next(i for i,o in enumerate(self) if value == key(o))
except StopIteration:
raise ValueError("{} is not in list".format(repr(value)))
def find_after_name(users, name):
i = extended_list(users).index(name, key=lambda u: u.name)
return users[(i+1) % len(users)]