Python中循环的重构

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

我目前正在开发一个iTunes数据程序,它不断地在用户的库中循环,以获取关于用户库的统计信息。 返回 我有一些代码片段如下:

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方法。谢谢你的帮助!我以前从未见过那部电影。这真的很容易知道。