Практика написания скриптов на Python

PythonPythonBeginner
Практиковаться сейчас

This tutorial is from open-source community. Access the source code

💡 Этот учебник переведен с английского с помощью ИИ. Чтобы просмотреть оригинал, вы можете перейти на английский оригинал

Введение

В этом разделе мы более тщательно рассмотрим практику написания сценариев на Python.

Что такое сценарий?

Сценарий - это программа, которая выполняет一系列语句 и завершается.

## program.py

statement1
statement2
statement3
...

До сих пор мы в основном писали сценарии.

Проблема

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

Определение вещей

Имена должны всегда быть определены, прежде чем их использовать впоследствии.

def square(x):
    return x*x

a = 42
b = a + 2     ## Требует, чтобы `a` было определено

z = square(b) ## Требует, чтобы `square` и `b` были определены

Порядок важен. Вы几乎总是把变量和函数的定义放在接近顶部的位置。

Определение функций

Хорошей идеей является размещение всего кода, связанного с одной задачей, в одном месте. Используйте функцию.

def read_prices(filename):
    prices = {}
    with open(filename) as f:
        f_csv = csv.reader(f)
        for row in f_csv:
            prices[row[0]] = float(row[1])
    return prices

Функция также упрощает повторяющиеся операции.

oldprices = read_prices('oldprices.csv')
newprices = read_prices('newprices.csv')

Что такое функция?

Функция - это именованная последовательность инструкций.

def funcname(args):
  statement
  statement
...
  return result

Любая инструкция Python может использоваться внутри.

def foo():
    import math
    print(math.sqrt(2))
    help(math)

В Python нет особенных инструкций (что делает ее легкой для запоминания).

Определение функции

Функции могут быть определены в любом порядке.

def foo(x):
    bar(x)

def bar(x):
    statements

## ИЛИ
def bar(x):
    statements

def foo(x):
    bar(x)

Функции должны быть определены только перед тем, как они будут использованы (или вызваны) во время выполнения программы.

foo(3)        ## foo должен быть уже определен

С точки зрения стиля, более распространено определять функции в низу-вверх порядке.

Стиль отbottom вверх

Функции рассматриваются как строительные блоки. Маленькие/простые блоки идут первыми.

## myprogram.py
def foo(x):
 ...

def bar(x):
 ...
    foo(x)          ## Определено выше
 ...

def spam(x):
 ...
    bar(x)          ## Определено выше
 ...

spam(42)            ## Код, использующий функции, появляется в конце

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

Конструирование функций

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

Строки документации (Doc Strings)

Хорошей практикой является включение документации в виде doc-строки. Doc-строки - это строки, записанные сразу после имени функции. Они используются для help(), IDE и других инструментов.

def read_prices(filename):
    '''
    Read prices from a CSV file of name,price data
    '''
    prices = {}
    with open(filename) as f:
        f_csv = csv.reader(f)
        for row in f_csv:
            prices[row[0]] = float(row[1])
    return prices

Хорошей практикой при написании doc-строк является написание короткого однострочного резюме того, что делает функция. Если требуется больше информации, включите короткий пример использования, а также более подробное описание аргументов.

Аннотации типов

Вы также можете добавить необязательные подсказки типов к определениям функций.

def read_prices(filename: str) -> dict:
    '''
    Read prices from a CSV file of name,price data
    '''
    prices = {}
    with open(filename) as f:
        f_csv = csv.reader(f)
        for row in f_csv:
            prices[row[0]] = float(row[1])
    return prices

Подсказки не влияют на работу программы. Они являются чисто информативными. Однако ими могут пользоваться IDE, программы проверки кода и другие инструменты для более глубокого анализа.

В разделе 2 вы написали программу report.py, которая выводила отчет о производительности портфеля акций. Эта программа состояла из нескольких функций. Например:

## report.py
import csv

def read_portfolio(filename):
    '''
    Read a stock portfolio file into a list of dictionaries with keys
    name, shares, and price.
    '''
    portfolio = []
    with open(filename) as f:
        rows = csv.reader(f)
        headers = next(rows)

        for row in rows:
            record = dict(zip(headers, row))
            stock = {
                'name' : record['name'],
               'shares' : int(record['shares']),
                'price' : float(record['price'])
            }
            portfolio.append(stock)
    return portfolio
...

Однако в программе также были участки, которые выполняли серию заранее заданных вычислений. Этот код располагался в конце программы. Например:

...

## Output the report

headers = ('Name', 'Shares', 'Price', 'Change')
print('%10s %10s %10s %10s'  % headers)
print(('-' * 10 +'') * len(headers))
for row in report:
    print('%10s %10d %10.2f %10.2f' % row)
...

В этом упражнении мы собираемся взять эту программу и организовать ее более четко вокруг использования функций.

Упражнение 3.1: Структурирование программы в виде коллекции функций

Измените программу report.py так, чтобы все основные операции, включая вычисления и вывод, выполнялись с использованием коллекции функций. Specifically:

  • Создайте функцию print_report(report), которая выводит отчет.
  • Измените последнюю часть программы так, чтобы она состояла только из серии вызовов функций и не содержала других вычислений.
✨ Проверить решение и практиковаться

Упражнение 3.2: Создание верхнего уровня функции для выполнения программы

Возьмите последнюю часть вашей программы и упакуйте ее в отдельную функцию portfolio_report(portfolio_filename, prices_filename). Сделайте так, чтобы вызов этой функции создавал отчет как и раньше:

portfolio_report('/home/labex/project/portfolio.csv', '/home/labex/project/prices.csv')

В этой окончательной версии ваша программа будет состоять только из серии определений функций, за которыми следует единственный вызов функции portfolio_report() в конце (который выполняет все шаги, связанные с программой).

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

>>> portfolio_report('/home/labex/project/portfolio2.csv', '/home/labex/project/prices.csv')
... look at the output...
>>> files = ['/home/labex/project/portfolio.csv', '/home/labex/project/portfolio2.csv']
>>> for name in files:
        print(f'{name:-^43s}')
        portfolio_report(name, '/home/labex/project/prices.csv')
        print()

... look at the output...
>>>
✨ Проверить решение и практиковаться

Комментарий

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

Резюме

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