Структурирование вызовов функций в Python - PullRequest
0 голосов
/ 07 ноября 2018

Я не знаю, как структурировать вызовы функций. Я знаю, что «правила чистого кода» говорят, что функции должны делать только ОДНУ вещь и что вы должны держать их как можно меньше. Так и я.

Но я не уверен, как вызывать эти функции. Я не знаю, как назвать понятия, но я изо всех сил стараюсь выбирать между «вызовом функций внутри функций» или «функциями цепочки / конвейеризации». В приведенном ниже примере у меня есть очень простой пример, но я надеюсь, что он прояснит концепции, о которых я говорю.

«Chaining / Piping» упрощает код для модульного тестирования, вам не нужно имитировать функции. Но «вызов внутренних функций» делает код короче и, возможно, более читабельным.

Как вы выбираете тот или иной путь?

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

# Calling inside a function

def sum_of_squares(numbers):
    return sum([square(num) for num in numbers])

def square(num):
    return num*num

numbers = [2,3,4]
square_sum = sum_of_squares(numbers))

print(square_sum) # prints 29

-----

# Chaining/Piping

def sum_of_squares(squares):
    return sum(squares)

def square(num):
    return num*num

numbers = [2,3,4]
squares = [ square(num) for num in numbers ]
square_sum = sum_of_squares(squares))

print(square_sum) # prints 29

Ответы [ 3 ]

0 голосов
/ 07 ноября 2018

По моему мнению, идея чистого кода означает, что код должен быть согласованным и читаемым для других. Вот что, я думаю, сделает код чистым и читабельным.

Тот, кто не работал над кодом или не имеет предварительных знаний о том, что делает код должен быть в состоянии понять, что конец результат выдаст код при запуске без необходимости объясните им, что делает ваш код.

Итак, для меня квадратная функция не нужна. number * number говорит само за себя.

Так что моя сумма квадратов будет

numbers = [2, 3, 4]
square_sum = sum(number * number for number in numbers)

ИЛИ если вам нужна квадратная функция

numbers = [2, 3, 4]
sqaureFunction = lambda number: number * number
square_sum = sum(map(sqaureFunction, numbers))

В таких небольших программах это не имеет значения, и вам не следует тратить время на то, чтобы ломать голову над чистым кодом, потому что эта программа ничего не делает. Итак, я собираюсь взять более крупный пример, чтобы объяснить это ...

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

  • Скачать HTML с сайта.
  • Передайте его парсерам HTML, таким как bs4 или lxml.
  • Перемещайтесь по каждой метке в супе HTML и извлекайте информацию.
  • Храните его.

Теперь для подробной части,

Часть 1 анализа HTML: Извлечение

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

Часть 2 парсинга HTML: парсер

  • Загрузить HTML из файла
  • Перейдите в HTML и получите необходимые данные
  • Хранить информацию в базе данных или файле.

Наконец, код:

Я просто собираюсь написать код для извлечения части здесь. См. документацию к функции makeHttpRequest до понимание концепции чистого кода

def makeGetRequest(url, headers = []):
    # You may want to preprocess. Add default headers, etc ... which are specific to the site.
    response = requests.get(url, headers=headers, params=payload)
    return response

def makeHttpRequest(url, method = "GET", headers = [], payload = {}):
    """
    This function makes an HTTP request.

    You may wanna do several requests before getting the actual information.
    But every time you're going to call make request because it does one thing i.e. Making a request. 
    There can be 101 things that can happen inside this function. 
    But on the outlook, it is just making a single request which can considered as 1 task.
    """
    # You may wanna do POST, PUT, PATCH. But this is just for giving general idea
    if method == "GET":
        response = makeGetRequest(url, headers=headers)
        return response.text
    return None

def extractor(storageFilename):
    """
    This function will make HTTP request and get HTML data.
    After getting HTML data it will store it in a file.
    """
    (url, headers, payload) = loadRequestInformation()
    html = makeHttpRequest(url, headers=headers, payload=payload)
    with open(storageFilename) as htmlFileHandle:
        htmlFileHandle.write(html)
0 голосов
/ 07 ноября 2018

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

0 голосов
/ 07 ноября 2018

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

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

С другой стороны, если вы «связываете» много вызовов функций, как при втором способе, тогда передняя часть, основной поток кода будет расти и через некоторое время станет менее прозрачной. Мое эмпирическое правило, которое всегда держит «впереди» то, что действительно достаточно для понимания вашего кода в последнее время, и всегда выбирает простую понятную для себя функцию именования соглашение, чтобы иметь возможность хорошо читать ваш код позже.

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

Надеюсь, это поможет.

...