Программирование — мощный инструмент. Python — это простой способ освоить важный навык

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

Программирование — это мощный навык, а Python — это простая отправная точка.

Не теряя много времени, этот пост предлагает от нуля до сотни с Python… с некоторыми советами в конце.

Вот демонстрация видео:

Печать и переменные

Урок №1 (или 0…), печать в консоль. Открываем интерпретатор Python:

$ pipenv run ipython

И пишем нашу пресловутую первую строчку кода.

print("Hello World")
>>> Hello World

Далее мы узнаем о комментировании кода.

# this is a comment and will not be 
# evaluated like code

Переменные хранят значения.

number = 10
string = "This is a string"
print(number)
>>> 10
print(string)
>>> "This is a string"

Мы можем использовать f-strings для печати нашего кода в строке, используя фигурные скобки для заключения имен наших переменных или кода Python.

print(f"number: {number}, and string: {string}")
>>> "number: 10, and string: This is a string"

Переменные не одинаковы. У них есть типы. Например, number — это целое число, а string — это набор символов, называемый строкой.

print(f"{type(number} and {type(string)}")
>>> "<class 'int'> and <class 'str'>"

Функции

Если у нас есть код, который мы должны писать снова и снова, рекомендуется не повторяться. Вместо того, чтобы повторяться, мы помещаем этот код в функцию и вызываем эту функцию. Давайте создадим функцию для последовательности Фибоначчи, в которую мы передаем число n, и это даст последовательность nth

# the fibonacci sequence
# 0, 1, 1, 2, 3, 5, 8...
def f(n: int) -> int:
		"""
		Fibonacci sequence
		---
		params
		n (int) number requested in sequence
		returns
		integer in fibonacci sequence
		"""
		if n == 0:
				return 0
		elif n == 1 or n == 2:
				return 1
		else:
				return f(n-1) + f(n-2)

В приведенном выше коде есть ряд понятий. Во-первых, мы вводим функцию, используя def. Мы используем типизацию, где мы принимаем n как целое число (n: int), и функция возвращает целое число (-> n).

Мы включаем строку документа, заключенную в тройные кавычки (""). Основная цель doc-string — объяснить, что делает функция.

Кроме того, эта функция является хорошим примером условного оператора. Операторы if проверяют, является ли следующее True или False. Если True, то кодовый блок выполняется.

ПРИМЕЧАНИЕ. Отступы в Python очень важны.

Если оператор if равен False, то проверяется elif (сокращение от else-if). Мы видим, что использование or означает, что только n == 1 или n == 2 должны быть истинными для срабатывания блока кода, в данном случае возвращается 1. Наконец, else запускается, когда предыдущие операторы if и elif равны False.

Эта строка f(n-1) + f(n-2) является примером рекурсии. Функция f вызывает сама себя, но с одним меньше n и двумя меньше n.

Дополнительные типы данных — списки

Мы можем многое сделать с функциями и переменными. Давайте рассмотрим различные типы данных в Python, начиная со списков.

Настраиваем диапазон.

r = range(10)
r
>>> range(0, 10) # number from 0 up to and not including 10

Создаем список.

lst = list(r)
lst
>>> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

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

lst[0] # the first element of the list is "0th" index
>>> 0 
lst[1:3] # the second and up to and not including the 3rd element
>>> [1, 2]
lst[-1] # the last element
>>> 9
lst[::-1] # reverses the list
>>> [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

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

def f1(x: int) -> str:
    return f"f1: {x + 1}"
def f2(x: int) -> str:
    return f"f2: {x ** 2}"
def f3(x: int) -> str:
    return f"f3: {1 / x}"
funcs = [f1, f2, f3]
for func in funcs:
    print(func(2))
>>> "f1: 3"
>>> "f2: 4"
>>> "f3: 0.5"

Это хороший способ представить пресловутый цикл for. Список является примером итератора. Например, мы можем использовать цикл for для перебора списка.

for i in lst:
		output = f(i)
		print(output, end=", ")
>>> "0, 1, 1, 2, 3, 5, 8, 13, 21, 34"

Вместо того, чтобы печатать список чисел, мы можем вернуть список.

output = []
for i in lst:
		output.append(f(i))
output
>>> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

Вместо того, чтобы создавать пустой список и использовать несколько строк, мы можем превратить его в «однострочный». Мы используем печально известное понимание списков.

output = [f(i) for i in lst]
output
>>> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

Импорт библиотек

Большую часть времени мы будем использовать хорошо написанный код других людей. Это экономит время, но также на этой планете есть очень умные люди. Они предлагают решения проблем, которые я не мог решить в своей жизни БЕСПЛАТНО. Одним из примеров является библиотека под названием pandas, популярная в области анализа данных.

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

Мы можем показать это, используя магическую функцию IPython, %timeit.

times = []
for i in range(33): # we will do the first 32 numbers in sequence
		t = %timeit -n1 -o f(i)
		times.append(t.best)

Тег -n<N> означает, сколько раз функция будет зацикливаться, в нашем случае только один раз; -o выводит результат timeit в переменную, в нашем случае t, для последующей проверки.

Теперь мы будем использовать pandas для табулирования наших данных.

import pandas as pd
df = pd.DataFrame(data=times, columns=["f"]
df.head() # prints first five results by default
>>>
f
0  5.829997e-07
1  6.790001e-07
2  7.760000e-07
3  1.695000e-06
4  2.799000e-06

Мы дали pandas псевдоним pd. Это избавляет нас от необходимости постоянно писать «панды».

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

import matplotlib.pyplot as plt
g = df.plot(title="Time on Fib")
g.set_xlabel("n")
g.set_ylabel("times (s)")
plt.savefig("f.png")

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

Мы можем рассчитать общее время:

df.sum()
>>> f    3.693825 # total time
>>> dtype: float64

Дополнительные типы данных — Словари

Мы представим словари. Словари имеют пару ключ и значение.

dct = {
    "key1": "value1",
    "key2": ["value2", "value3"],
    "random name for a key": 1,
    "key4": (2000, 3000)
}

И чтобы получить значение из словаря:

dct["key4"]
>>> (2000, 3000) # this is a tuple by the way

Если мы ссылаемся на несуществующий ключ (например, dct["key5"]), мы получаем ошибку. В качестве альтернативы мы можем использовать метод get(), чтобы наш код не выдавал ошибку и вместо этого возвращал None.

dct.get("key5") is None
>>> True

Мы можем даже применить цикл for к словарям.

for k, v in dct.items():
		print(f"key: {k}, value: {v}")
>>> key: key1, value: value1
>>> key: key2, value: ['value2', 'value3']
>>> key: key3, value: 1
>>> key: key4, value: (2000, 3000)

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

cache = {}
def f_better(n: int) -> int:
    """
    Fibonacci sequence - !!with caching!!
    ---
    params
        n (int) number requested in sequence
    
    returns
        interger in fibonacci sequence
    """
    if all([cache.get(n-1), cache.get(n-2)]):
        return cache[n-1] + cache[n-2]   
    elif n == 0:
        return 0
    elif n == 1 or n == 2:
        return 1
    else:
        cache[n] = f_better(n-1) + f_better(n-2) 
        return cache[n]

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

Еще одним дополнением является то, что вновь вычисленное значение сохраняется в кэше.

Мы можем измерить, как долго работает эта новая функция.

times = []
for i in range(35):
    t = %timeit -n1 -o -q f_better(i)
    times.append(t.best)
    
df['f_better'] = pd.DataFrame(data=times)
df.tail(10)
>>>
f             f_better
23  0.005453  5.040001e-07
24  0.008786  1.269000e-05
25  0.014167  1.357200e-05
26  0.022997  4.909998e-07
27  0.037203  1.430600e-05
28  0.060064  1.520500e-05
29  0.097307  5.379998e-07
30  0.157304  1.653000e-05
31  0.254740  1.687500e-05
32  0.412715  5.299999e-07

Мы можем построить это.

g = df.plot(title="Comparing")
g.set_xlabel("n")
g.set_ylabel("time (s)")
plt.savefig("f_better.png")

Мы видим, что время, затрачиваемое на эту реализацию кэширования, значительно меньше.

Наконец, мы можем напечатать это cache.

cache
>>> {        
                 3: 2,
		 4: 3,
		 6: 8,
		 7: 13,
		 9: 34,
		 10: 55,
		 12: 144,
		 13: 233,
		 15: 610,
		 16: 987,
		 18: 2584,
		 19: 4181,
		 21: 10946,
		 22: 17711,
		 24: 46368,
		 25: 75025,
		 27: 196418,
		 28: 317811,
		 30: 832040,
		 31: 1346269,
		 33: 3524578,
		 34: 5702887
}

Классы

В Python все является объектами. Для создания собственных объектов мы создаем classes.

class Car:
    """
    Class for cars that people drive
    ---
    params 
        make (str): make of the car
        model (str): model of the car
        year (int): year car was made
    """
    def __init__(self, make: str, model: str, year: int=2021):
        self.make = make
        self.model = model
        self.year = year
        self.__is_driving = False
        
    def __repr__(self):
        return f'{self.make} - {self.model}'
    
    def drive(self) -> str:
        if self.__is_driving == False:
            print(f'{self.make} - {self.model} is now driving')
            self.__is_driving = True
        else:
            print(f'{self.make} - {self.model} is already driving!')
    
    def stop(self) -> str:
        if self.__is_driving == True:
            print(f'{self.make} - {self.model} has stopped')
            self.__is_driving = False
        else:
            print(f'{self.make} - {self.model} is already stopped!')

Давайте протестируем наш новый класс.

shivan = Car('Toyota', 'Corolla', '2008')
bruno = Car('Tesla', 'Model3')

У нас есть два объекта, shivan и bruno, производные от класса Car.

bruno
>>> "Tesla - Model3"
shivan.drive()
>>> "Toyota - Corolla is now driving"
shivan.drive()
>>> "Toyota - Corolla is ALREADY driving"
shivan.stop()
>>> "Toyota - Corolla has stopped"
bruno.stop()
>>> "Tesla - Model3 is ALREADY stopped!"

Заключительные примечания

Итак, вы ознакомились с основами Python. Это только начало. Иди строй что-нибудь. Иди будь кем-нибудь.

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

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

Ка кайт Ано!