Установка «модов» с помощью Python

Немного оговорки: у вас будет полевой день с этим. Это ужасно. Глядя на это, я хочу рвать, и не прошу объяснить это, потому что я давно забыл, о чем думал, когда писал. Это, как говорится, я не могу найти способ сократить его, чтобы это имело смысл. Может быть, кто-то может помочь мне?

Немного фона:

Эта программа предназначена для установки модов для игры под названием Supreme Commander 2. Она предназначена для установки ЛЮБОГО мод. Для всех модов справедливо следующее:

  • Единственным типом архива является .scd. SCD имеют одинаковую кодировку как .zip, но со скоростью сжатия 0%.
  • Способ установки мода прост: в первом всегда будет по крайней мере один scd, и внутри него может быть больше scd. Любой scd с папками внутри него должен быть перемещен в

      

    C: \ Program Files (x86) \ Steam \ steamapps \ common \ supreme commander 2 \ gamedata ​​p>

    Для мод может быть больше одного scd.

И все. Это что-то вроде «проблемы программирования», но я действительно ищу улучшения в следующем коде:

import os, zipfile
def installfromdownload(filename):
    global scdlist
    targetfolder = r"C:\Program Files (x86)\Mod Manager\Mods\f_" + filename[:-4]
    lentf = len(targetfolder)
    unzip(r"C:\Program Files (x86)\Mod Manager\Mods\z_" + filename + ".scd", targetfolder)
    if checkdirs(targetfolder) == False:
        os.system("copy C:\Program Files (x86)\Mod Manager\Mods\z_" + filename + ".scd" " C:\Program Files (x86)\Steam\steamapps\common\supreme commander 2\gamedata")
    else:
        newfolderonelist = []
        getscds(targetfolder)
        for file in scdlist:
            newfolderone = targetfolder + "\f_" + file[:-4]
            filepath = targetfolder + "\\" + file
            unzip(filepath, newfolderone)
            newfolderonelist.append(newfolderone)
        for newfolders in newfolderonelist:
            if os.checkdirs(newfolders) == False:
                newfolders = newfolders[lentf + 1:]
                for scds in scdlist:
                    if newfolder in scds == True:
                        scdpath = targetfolder + "\\" + scds
                        os.system("copy " + scdpath + " C:\Program Files (x86)\Steam\steamapps\common\supreme commander 2\gamedata")

Обратите внимание, что здесь нет обработки ошибок, которая выполняется при вызове функции. Функция unzip() находится здесь:

def unzip(file, target):
    modscd = zipfile.ZipFile(file)
    makepath(target)
    modscd.extractall(target)

Функция checkdirs() находится здесь (обратите внимание, что это довольно плохо, она проверяет только периоды в имени файла, поэтому, пожалуйста, отправьте предложения ):

def checkdirs(target):
    isfile = 0
    targetlist = os.listdir(target)
    for files in targetlist:
        for letters in files:
            if letters == ".":
                isfile = isfile + 1
    if isfile == len(os.listdir(target)):
        return False
    else:
        return True

Функция getscds() просто проверяет имя файла для «.scd».

11 голосов | спросил KnightOfNi 13 FebruaryEurope/MoscowbThu, 13 Feb 2014 08:11:49 +0400000000amThu, 13 Feb 2014 08:11:49 +040014 2014, 08:11:49

3 ответа


6

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

def copy_scds(scd, target_folder):
    unzip scd to target_folder

    if target_folder has scds:
        for each inner_scd:
            subfolder = new folder under target_folder
            copy_scds(inner_scd, subfolder)
    else:
        copy scd to gamedata

Как вы можете видеть, части распаковки и копирования записываются только один раз, а не повторяются во вложенных циклах. Дополнительные scds и подпапки просто передаются как новые аргументы для scd и target_folder.

Основная функция тогда должна только определить начальный «корень» scd и target_folder и начните процесс:

def install(filename):
    scd = r"C:\Program Files (x86)\Mod Manager\Mods\z_" + filename + ".scd"
    target_folder = r"C:\Program Files (x86)\Mod Manager\Mods\f_" + filename[:-4]

    copy_scds(scd, target_folder)

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

mods_folder = r"C:\Program Files (x86)\Mod Manager\Mods"
zip_prefix = "z_"
output_prefix = "f_"
gamedata_folder = r"C:\Program Files (x86)\Steam\steamapps\common\supreme commander 2\gamedata"

На самом деле, возможно, удастся полностью удалить необходимость настройки игровой папки. Steam знает, как запустить любую игру, основанную только на уникальном AppID , поэтому теоретически должен быть какой-то способ автоматического определения пути .

ответил nmclean 13 FebruaryEurope/MoscowbThu, 13 Feb 2014 18:59:50 +0400000000pmThu, 13 Feb 2014 18:59:50 +040014 2014, 18:59:50
13

Во-первых, ваша функция installfromdownload должна быть install_from_download (и у вас есть docstring, чтобы он объяснил, что он делает).

Эта строка:

targetfolder = r"C:\Program Files (x86)\Mod Manager\Mods\f_" + filename[:-4]

должен, вероятно, проверить и убедиться, что файл на самом деле заканчивается на .zip (или в любом формате, который вы на самом деле ожидаете, чтобы он появился ):

if filename.endswith('.zip'):
    target_folder = ...

С чем-то не получается:

unzip(r"C:\Program Files (x86)\Mod Manager\Mods\z_" + filename + ".scd", targetfolder)

Вы удаляете последние 4 символа с имени файла раньше, и здесь вы добавляете «.scd». Я предположил, что вы отключили .scd, но потом поняли, что вы разархивируете его, поэтому на самом деле это .zip (или что-то в этом роде). В любом случае, это запутывает и что-то вроде:

target_folder = r"..." + filename.split('.zip')[0] 

сделает намного более понятным то, что вы на самом деле делаете (комментарий слишком поможет).

Не проверяйте явно False:

if checkdirs(targetfolder) == False:

Это должно быть:

if not checkdirs(targetfolder):

Не присоединяйте пути вместе с "\\" (это зависит от ОС):

 filepath = targetfolder + "\\" + file

Вместо этого используйте os.path.join:

 filepath = os.path.join(target_folder, file)

Некоторые из имен переменных здесь делают ваш код действительно, очень трудно следовать (newfolderonelist, newfolderone).

Эта строка должна привести к сбою вашего сценария:

if os.checkdirs(newfolders) == False:

Как вы пытаетесь вызвать функцию checkdirs (os.checkdirs не существует).

Посмотрим на функцию checkdirs:

def checkdirs(target):
    isfile = 0
    targetlist = os.listdir(target)
    for files in targetlist:
        for letters in files:
            if letters == ".":
                isfile = isfile + 1
if isfile == len(os.listdir(target)):
    return False
else:
    return True

Снова следуя соглашению об именах Python, это должно быть check_dirs (хотя для названия требуется много работы - для чего он их проверяет? ). Кроме того, здесь большая часть работы может быть заменена на os.path.isfile. Из чтения этого, похоже, вы проверяете, что все в данном каталоге не является файлом, поэтому я назовю его not_all_files:

def not_all_files(directory):
    '''Checks the given directory, returning False if it contains only files,
    and True otherwise. If the argument given is not a directory, 
    raises IOError.
    '''

    if not os.path.isdir(directory)
        # Oops, passed in something that wasn't a directory
        # Handle this error
        raise IOError('....')
    return not all((os.path.isfile(f) for f in os.listdir(directory)))

Учитывая, как вы используете его выше, я бы предпочел обменять то, что он возвращает: вернуть его True, если все в каталог - это файл, False в противном случае. Тогда это будет:

def all_files(directory):
    '''Checks the given directory, returning True if it contains only files,
    and False otherwise. If the argument given is not a directory, 
    raises IOError.
    '''

    if not os.path.isdir(directory)
        # Oops, passed in something that wasn't a directory
        # Handle this error
        raise IOError('....')
    return all((os.path.isfile(f) for f in os.listdir(directory)))

Тогда ваш код в install_from_download станет:

 if all_files(target_folder):

, который, как мне кажется, гораздо легче читать.

ответил Yuushi 13 FebruaryEurope/MoscowbThu, 13 Feb 2014 08:58:13 +0400000000amThu, 13 Feb 2014 08:58:13 +040014 2014, 08:58:13
8

В дополнение к ответу Yuushi, я замечаю, что вы используете os.system для копирования файлов. Это небезопасно и не нужно. Если все, что вам нужно сделать, это скопировать файл, используйте shutil.copy

Переведенный, немного вашего кода может выглядеть так:

# assuming shutil is imported
shutil.copy(r"C:\Program Files (x86)\Mod Manager\Mods\z_" + filename + ".scd",
            r"C:\Program Files (x86)\Steam\steamapps\common\supreme commander 2\gamedata")

Конечно, вы должны изменить этот код, чтобы использовать os.path.join, как это было предложено в ответе Yuushi.

ответил icktoofay 13 FebruaryEurope/MoscowbThu, 13 Feb 2014 09:12:37 +0400000000amThu, 13 Feb 2014 09:12:37 +040014 2014, 09:12:37

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132