Python中循环的重构
我目前正在开发一个iTunes数据程序,它不断地在用户的库中循环,以获取关于用户库的统计信息。 返回 我有一些代码片段如下:Python中循环的重构,python,python-3.x,refactoring,control-flow,Python,Python 3.x,Refactoring,Control Flow,我目前正在开发一个iTunes数据程序,它不断地在用户的库中循环,以获取关于用户库的统计信息。 返回 我有一些代码片段如下: def numArtist(self): num = 0 for song in self.allSongs: tempList = [] if song.artist not in tempList: tempList.append(song.artist) num += 1
def numArtist(self):
num = 0
for song in self.allSongs:
tempList = []
if song.artist not in tempList:
tempList.append(song.artist)
num += 1
return num
def getAlbumNames(self):
albums = []
for song in self.allSongs:
if song.album not in albums:
albums.append(song.album)
return albums
在重复主for循环体的情况下:
for song in self.allSongs: # same for-loop condition
# different for-loop body
有没有办法重构像这样的方法,我有相同的for循环条件,但有不同的body定义
我有很多方法使用相同的for循环,所以我想找到一种方法来降低代码的复杂性和冗余度
仅供参考,所有歌曲对象都有我用来获取数据的属性——艺术家、专辑(名称)、流派等 如果您的
所有歌曲
列表的内容是不可变的(我怀疑是不可变的),您可以将列表
s转换为集
s,然后再次转换回列表
s,或者使用集理解来消除重复项。然后,您的功能可以大大简化,如下所示:
def numArtist(self):
return len({song.artist for sing in self.allSongs})
def getAlbumNames(self):
return list({song.album for song in self.allSongs})
如果您不确定歌曲
对象是否可变,请尝试此方法。如果它们是可变对象,则会出现如下异常:
TypeError: unhashable type: ...
如果这算是有效的“for循环重构”,则可以对这两个代码段使用集合理解:
使用
getattr的通用版本
get_values = lambda objs, attr: {getattr(obj, attr) for obj in objs
attributes = 'artist', 'album'
values = [get_values(self.allSongs, name) for name in attributes]
artists, albums = values
artist_count = len(artists)
get_artist = lambda song: song.artist
get_album = lambda song: song.album
getters = get_artist, get_album
values = [
{func(song) for song in self.allSongs}
for getter in getters
]
artists, albums = values
artist_count = len(artists)
# If `song` is an instance of the `Song` class and both `artist` and
# `album` are properties defined on the class, it's also possible to
# directly use the property getter (`property.fget`) to avoid defining
# the lambdas manually:
get_artist = Song.artist.fget
get_album = Song.album.fget
... # <same as above>
使用lambda的通用版本
get_values = lambda objs, attr: {getattr(obj, attr) for obj in objs
attributes = 'artist', 'album'
values = [get_values(self.allSongs, name) for name in attributes]
artists, albums = values
artist_count = len(artists)
get_artist = lambda song: song.artist
get_album = lambda song: song.album
getters = get_artist, get_album
values = [
{func(song) for song in self.allSongs}
for getter in getters
]
artists, albums = values
artist_count = len(artists)
# If `song` is an instance of the `Song` class and both `artist` and
# `album` are properties defined on the class, it's also possible to
# directly use the property getter (`property.fget`) to avoid defining
# the lambdas manually:
get_artist = Song.artist.fget
get_album = Song.album.fget
... # <same as above>
使用属性的通用版本
get_values = lambda objs, attr: {getattr(obj, attr) for obj in objs
attributes = 'artist', 'album'
values = [get_values(self.allSongs, name) for name in attributes]
artists, albums = values
artist_count = len(artists)
get_artist = lambda song: song.artist
get_album = lambda song: song.album
getters = get_artist, get_album
values = [
{func(song) for song in self.allSongs}
for getter in getters
]
artists, albums = values
artist_count = len(artists)
# If `song` is an instance of the `Song` class and both `artist` and
# `album` are properties defined on the class, it's also possible to
# directly use the property getter (`property.fget`) to avoid defining
# the lambdas manually:
get_artist = Song.artist.fget
get_album = Song.album.fget
... # <same as above>
#如果'song'是'song'类的实例,并且'artist'和'
#'album'是类上定义的属性,也可以
#直接使用属性getter(`property.fget`)避免定义
#lambda手动执行以下操作:
get_artist=Song.artist.fget
get_album=Song.album.fget
... #
使用和len
来简化它们:
def numArtist(self):
return len({song.artist for song in self.allSongs})
def getAlbumNames(self):
return {song.album for song in self.allSongs}
为了使其更通用,您可以编写一个方法,该方法采用a,并使用该方法从每首歌曲中过滤出属性:
def uniqueProps(self, fxn):
return {fxn(song) for song in self.allSongs}
def getAlbumNames(self):
return self.uniqueProps(lambda song: song.album)
您可以尝试创建生成器,生成歌曲属性的值。让我举个例子:
def gen_attr(songs, attr_name):
for song in songs:
yield getattr(song, attr_name)
class Song(object):
def __init__(self, name, artist):
self.name = name
self.artist = artist
class Album(object):
def __init__(self, songs_list):
self.songs_list = songs_list
def allSongs(self):
return self.songs_list
s = Song('Ahoy', 'Pirate')
s1 = Song('Bye', 'My Son')
s2 = Song('Ahoy', 'Captain')
a = Album([s, s1])
现在,如果要获取所有歌曲名称,可以使用:
song_names = list(gen_attr(a.allSongs(), 'name'))
print(song_names) # ['Ahoy', 'Bye', 'Ahoy']
对于不重复的歌曲名称,您将使用:
song_names = list(set(gen_attr(a.allSongs(), 'name')))
print(song_names) # ['Ahoy', 'Bye']
artists = len(set(gen_attr(a.allSongs(), 'artist')))
要计算未重复的艺术家姓名,请使用:
song_names = list(set(gen_attr(a.allSongs(), 'name')))
print(song_names) # ['Ahoy', 'Bye']
artists = len(set(gen_attr(a.allSongs(), 'artist')))
要创建艺术家列表,只需选择:
artists = list(gen_attr(a.allSongs(), 'artist'))
print(artists) # ['Pirate', 'My Son', 'Captain']
您在numArtist
中有一个bug:它在每首歌曲上重新分配[]
到templast
。此外,您应该使用集合,而不是列表,因为艺术家的顺序并不重要。最后,len(templast)
将计算templast
中的项目数,这样您就不必跟踪num
。您可以在下面查看我的答案。在我看来,所有带有列表理解的答案都是错误的,因为它们与for循环的工作方式完全相同,但都是作为理解编写的。@Nf4r-您的解决方案还使用for
循环来迭代所有歌曲。这与其他答案有什么根本性的不同?这不是很明显吗?我不会像其他人那样创建额外的代码来生成数据。我有一个for循环,可以根据给定的attr\u name
生成不同的数据,而无需像其他人一样将代码复制粘贴到其他任何地方。我认为很容易看到,我已经创建了一个简单的生成器函数,用于生成值,而其他函数只是复制粘贴代码来更改列表中的属性名称。编辑以匹配预期行为-谢谢。我经常使用不可变
作为可散列
的同义词,我想你可能会认为这并不准确。@pistache我不太确定它是否意味着可散列,因为不可散列的对象可以在列表中……除非我遗漏了其他内容。我同意在第二个例子中,转换为列表是不必要的,我只是想匹配OPs方法。谢谢你的帮助!我以前从未见过那部电影。这真的很容易知道。