Очистка вложенных циклов в python - PullRequest
1 голос
/ 21 января 2010

У меня есть этот код:

def GetSteamAccts(): #Get list of steam logins on this computer.
    templist = []
    Steamapp_Folders = ["C:\\Program Files (x86)\\Steam\\steamapps\\", "C:\\Program Files\\Steam\\steamapps\\"] #Check both of these directories.
    for SF_i in range(len(Steamapp_Folders)):
        if os.path.exists(Steamapp_Folders[SF_i]): #If the directory even exists...
            Steam_AppDir_Items = os.listdir(Steamapp_Folders[SF_i]) #List items under steam install directory.
            for S_AD_i in range(len(Steam_AppDir_Items)): #Make sure the user doesn't have any files in here...
                if os.path.isdir(Steamapp_Folders + Steam_AppDir_Items[S_AD_i]): #If our path is a directory...
                    templist.append(Steam_AppDir_Items[S_AD_i])  #Add it to our list of logins.
                                                                 #(If some idiot puts extra folders in here,
                                                                 #it's their own damn fault when it shows on the list.)
    return templist #Return a (not so) properly filtered list of steam logins.

Моя проблема в том, что мне это ужасно уродливо. Я составил список из 2 путей (только один из них когда-либо будет существовать), перебрал эти пути, затем мне нужно получить список элементов в этих путях, а затем пройти по ним и отфильтровать не каталоги, чтобы получить псевдо-список Steam-логинов на компьютере пользователя. (По сути, просто получение списка любых существующих каталогов (только каталогов!) По любому из этих двух путей)

Есть ли более короткий способ сделать это (кроме уплотнения циклов в отдельные строки?)?

Я бы предпочел, чтобы мне было предложено англоязычное решение, чтобы я мог составить его самостоятельно; а не код. Это единственный способ, которым я действительно научусь правильно. Даже хороший маленький намек или выдержка, чтобы я мог сам разобраться, был бы хорош.

И: всегда ли нужно обходить списки в циклах:

for x in range(len(somelist)):

или есть что-то более короткое, чем использование диапазона (len (?

)

Ответы [ 5 ]

9 голосов
/ 21 января 2010
for i in range(len(somelist)):
   something( somelist[i] )

должно быть записано как

for x in somelist: 
    something( x )

Также вы можете написать все намного короче:

def GetSteamAccts():
    Steamapp_Folders = [f for f in ("C:\\Program Files (x86)\\Steam\\steamapps\\", 
                                    "C:\\Program Files\\Steam\\steamapps\\") 
                          if os.path.isdir(f)]
    return [os.path.join(root, folder) 
                    for root in Steamapp_Folders 
                    for folder in os.listdir(root) 
                    if os.path.isdir( os.path.join(root, folder)) ]

Это выглядит чище и фактически делает то, что вы хотели:; -)

def subfoldernames( root ):
    for folder in os.listdir(root):
        path = os.path.join(root, folder)
        if os.path.isdir(path):
            yield folder # just the name, not the path

# same, just shorter:
def subfoldernames( root ):
    # this returns a generator, written as a generator expression
    return ( folder for folder in os.listdir( root ) 
                    if os.path.isdir(os.path.join( root, folder )) )

def GetSteamAccts():
        Steamapp_Folders = ("C:\\Program Files (x86)\\Steam\\steamapps\\", 
                                             "C:\\Program Files\\Steam\\steamapps\\")
        for folder in Steamapp_Folders:
            if os.path.isdir(folder):
                # only the subfolders of the first path that exists are returned
                return list(subfoldernames( folder )) 
5 голосов
/ 21 января 2010

И: у списков для циклов всегда есть быть пройденным как:

для x в диапазоне (len (somelist)): или есть там что-то короче, чем с помощью диапазон (лен (?

Конечно, если вы хотите получить доступ только к элементу и вас не интересует его индекс, вы можете сделать это:

for x in somelist:

Если вы также хотите индекс, вы можете сделать это:

for index, x in enumerate(somelist):
4 голосов
/ 21 января 2010

Вы должны потерять идею, что цикл for всегда перебирает числа от нуля до чего-то, как в других языках. Вы просто просматриваете списки, поэтому используйте цикл for следующим образом:

def GetSteamAccts():
    templist = []
    Steamapp_Folders = ["C:\\Program Files (x86)\\Steam\\steamapps\\", "C:\\Program Files\\Steam\\steamapps\\"] #Check both of these directories.
    for steamapp_folder in Steamapp_Folders:
        if os.path.exists(steamapp_folder):
            for steam_appDir_item in os.listdir(steamapp_folder):
                if os.path.isdir(os.path.join(steamapp_folder, steam_appDir_item)):
                    templist.append(steam_appDir_item)
    return templist

В Python цикл for проходит через все, что может быть повторено. Для тех немногих случаев, когда вам действительно нужны цифры (и только цифры), есть range. Для тех случаев, когда вам нужны цифры и предметы, используйте это:

for number, item in enumerate(my_list):

При объединении for с и if с вы также должны взглянуть на выражения генератора и понять список (см. документы ).

2 голосов
/ 21 января 2010

Как уже упоминали другие, удаление предположения о целочисленных индексах устраняет большую сложность.

Пройдя немного дальше, вы можете заменить шаблоны, в которых вы строите список, добавив в цикл for понимание списка . Насколько (или насколько) эти улучшения читаемости спорны, когда вы попадаете на несколько уровней вложенности, но они делают код намного более кратким:

def GetSteamAccts():
    Steamapp_Folders = ["C:\\Program Files (x86)\\Steam\\steamapps\\", "C:\\Program Files\\Steam\\steamapps\\"] #Check both of these  directories.
    return [ item for folder in Steamapp_Folders if os.path.exists(folder)
                  for item in os.listdir(folder) if os.path.isdir(os.path.join(folder, item)) ]
0 голосов
/ 21 января 2010

Я бы предложил что-то вроде этого:

def check_subfolder(*args):
    for folder in args:
         for root, dirs, _ in os.walk(folder):
              return [os.path.join(root, i) for i in dirs]  # since only 1 will ever exist

это просто экономит на всех этих if утверждениях. Конечно, первый цикл for может быть удален из функции, потому что это не существенно для логики кода.

edit : обновлено, чтобы возвращать подкаталоги первого существующего каталога, согласно вопросу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...