使用 Spotipy/Spotify API 的 'else' 序列问题

Issue with 'else' sequence using Spotipy/Spotify API

我和我的团队(python 的新手)编写了以下代码来生成与特定城市和相关术语相关的 spotify 歌曲。 如果用户输入的城市不在我们的 CITY_KEY_WORDS 列表中,那么它会告诉用户输入将被添加到请求文件中,然后将输入写入文件。 代码如下:


from random import shuffle
from typing import Any, Dict, List
import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
sp = spotipy.Spotify(
    auth_manager=SpotifyClientCredentials(client_id="",
                                          client_secret="")
)
CITY_KEY_WORDS = {
    'london': ['big ben', 'fuse'],
    'paris': ['eiffel tower', 'notre dame', 'louvre'],
    'manhattan': ['new york', 'new york city', 'nyc', 'empire state', 'wall street', ],
    'rome': ['colosseum', 'roma', 'spanish steps', 'pantheon', 'sistine chapel', 'vatican'],
    'berlin': ['berghain', 'berlin wall'],
}

def main(city: str, num_songs: int) -> List[Dict[str, Any]]:
    if city in CITY_KEY_WORDS:
        """Searches Spotify for songs that are about `city`. Returns at most `num_songs` tracks."""
        results = []
        # Search for songs that have `city` in the title
        results += sp.search(city, limit=50)['tracks']['items']  # 50 is the maximum Spotify's API allows
        # Search for songs that have key words associated with `city`
        if city.lower() in CITY_KEY_WORDS.keys():
            for related_term in CITY_KEY_WORDS[city.lower()]:
                results += sp.search(related_term, limit=50)['tracks']['items']
        # Shuffle the results so that they are not ordered by key word and return at most `num_songs`
        shuffle(results)
        return results[: num_songs]
    else:
        print("Unfortunately, this city is not yet in our system. We will add it to our requests file.")
        with open('requests.txt', 'r') as text_file:
            request = text_file.read()
        request = request + city + '\n'
        with open('requests.txt', 'w+') as text_file:
            text_file.write(request)

def display_tracks(tracks: List[Dict[str, Any]]) -> None:
    """Prints the name, artist and URL of each track in `tracks`"""
    for num, track in enumerate(tracks):
        # Print the relevant details
        print(f"{num + 1}. {track['name']} - {track['artists'][0]['name']} {track['external_urls']['spotify']}")
if __name__ == '__main__':
    city = input("Virtual holiday city? ")
    number_of_songs = input("How many songs would you like? ")
    tracks = main(city, int(number_of_songs))
    display_tracks(tracks)

代码 运行 适用于“if”语句(如果有人进入我们列出的城市)。 但是当 else 语句是 运行 时,在执行完操作后会出现 2 个错误(它将用户的输入打印并写入文件)。

出现的错误是:

Traceback (most recent call last):
  File "...", line 48, in <module>
    display_tracks(tracks)
  File "...", line 41, in display_tracks
    for num, track in enumerate(tracks):
TypeError: 'NoneType' object is not iterable

请原谅我的知识匮乏,但有人可以帮助解决这个问题吗?

我们也想在最后创建一个歌曲的播放列表,但是在这方面遇到了困难。

当您的 if 语句被执行时,您 return 一个项目列表并将它们提供给 display_tracks() 函数。但是当执行 else 语句时会发生什么?您将请求添加到您的 text-file,但不要 return 任何东西(或 NoneType 项目)并将其提供给 display_tracks()display_tracks 然后迭代此 NoneType 项,抛出异常。

如果确实有任何曲目要显示,您只想显示曲目。一种方法是将 display_tracks() 的调用移动到 main 函数中,但是如果找不到 search-terms 的曲目,则会抛出相同的错误。另一种解决方案是首先检查您的 tracks 是否不为空,或者使用

之类的东西捕获 TypeError 异常
tracks = main(city, int(number_of_songs))
try:
    display_tracks(tracks)
except TypeError:
    pass

您的 main 函数在 else 子句中没有 return 语句,这导致 tracks 成为 None。在 tracksNone 时迭代 tracks 是导致错误的原因。 您可以做一些事情来改进代码:

  • 关注点分离:main 函数做两件不同的事情,检查输入和获取曲目。
  • 一开始就.lower()一次,这样你就不必重复了。
  • 遵循文档约定。
  • 使用前检查响应
  • 一些代码清理

请参阅下面我上面建议的更改:

def fetch_tracks(city: str, num_songs: int) -> List[Dict[str, Any]]:
    """Searches Spotify for songs that are about `city`.

    :param city: TODO: TBD
    :param num_songs:  TODO: TBD
    :return: at most `num_songs` tracks.
    """
    results = []
    for search_term in [city, *CITY_KEY_WORDS[city]]:
        response = sp.search(search_term, limit=50)
        if response and 'tracks' in response and 'items' in response['tracks']:
            results += response['tracks']['items']
    # Shuffle the results so that they are not ordered by key word and return
    # at most `num_songs`
    shuffle(results)
    return results[: num_songs]


def display_tracks(tracks: List[Dict[str, Any]]) -> None:
    """Prints the name, artist and URL of each track in `tracks`"""
    for num, track in enumerate(tracks):
        # Print the relevant details
        print(
            f"{num + 1}. {track['name']} - {track['artists'][0]['name']} "
            f"{track['external_urls']['spotify']}")


def main():
    city = input("Virtual holiday city? ")
    city = city.lower()
    # Check the input city and handle unsupported cities.
    if city not in CITY_KEY_WORDS:
        print("Unfortunately, this city is not yet in our system. "
              "We will add it to our requests file.")
        with open('requests.txt', 'a') as f:
            f.write(f"{city}\n")
        exit()

    number_of_songs = input("How many songs would you like? ")
    tracks = fetch_tracks(city, int(number_of_songs))
    display_tracks(tracks)


if __name__ == '__main__':
    main()