5 Хитростей Python, которые отличают Senior-разработчика от Juniors

5 Хитростей Python, которые отличают Senior-разработчика от Juniors

Каждый год, начиная с 2015 года, первого декабря стартует Advent of Code. Как описано на их веб-сайте, Advent of Code (далее AoC) - это

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

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

Чтобы проиллюстрировать концепции, я не буду вдаваться в решение полных головоломок AoC, а, скорее, сосредоточусь только на небольшой части конкретной головоломки, в которой Сениоров легко отличить от Джуниоров.

1. Эффективное считывание в файле со списковым включением и split

На первый день AoC требуется прочитать некоторое количество блоков чисел. Каждый блок разделён пустой строкой (таким образом, фактически '\n’).

Входные данные и желаемый результат

# INPUT 10 20 30 50 60 70 # DESIRED OUTPUT [[10, 20, 30], [50, 60 70]]

Подход Junior-разработчика: цикл с операторами if-else

numbers = [] with open("file.txt") as f: group = [] for line in f: if line == "\n": numbers.append(group) group = [] else: group.append(int(line.rstrip())) # append the last group because if line == "\n" will not be True for # the last group numbers.append(group)

Подход Senior-разработчика: использование спискового включения и .split()

with open("file.txt") as f: nums = [list(map(int, (line.split()))) for line in f.read().rstrip().split("\n\n")]

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

2. Использование Enum вместо if-elif-else

На второй день задача вращается вокруг игры в камень-ножницы-бумага. Другая выбранная форма (камень, бумага или ножницы) приводит к различному количеству очков: 1 (X), 2 (Y) и 3 (Z) соответственно. Ниже приведены два подхода к решению этой проблемы.

Входные данные и желаемый результат

# INPUT X Y Z # DESIRED OUTPUT 1 2 3

Подход Junior-разработчика: if-elif-else

def points_per_shape(shape: str) -> int: if shape == 'X': return 1 elif shape == 'Y': return 2 elif shape == 'Z': return 3 else: raise ValueError('Invalid shape')

Подход Senior-разработчика: Enum

from enum import Enum class ShapePoints(Enum): X = 1 Y = 2 Z = 3 def points_per_shape(shape: str) -> int: return ShapePoints[shape].value

Конечно, в этом примере наивный Байесовский алгоритм не так уж и ужасен, но использование Enum приводит к более короткому и удобочитаемому коду. Особенно, когда возможно больше вариантов, наивный алгоритм if-elif-else будет становиться всё хуже и хуже, в то время как с Enum вести обзор остаётся относительно легко. Для получения дополнительной информации о Enum нажмите здесь.

3. Использование таблиц поиска вместо словарей

На третий день буквы имели разные значения. Строчные буквы a-z имели значения от 1 до 26, а прописные буквы a-z от 27 до 52. Из-за множества различных возможных значений использование Enum, подобного приведённому выше, привело бы к большому количеству строк кода. Более практичным подходом здесь является использование таблицы поиска:

# INPUT c Z a ... # DESIRED OUPUT 3 52 1 ...

Подход Junior-разработчика: создание глобального словаря

letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' letter_dict = dict() for value, letter in enumerate(letters, start=1): letter_dict[letter] = value def letter_value(ltr: str) -> int: return letter_dict[ltr]

Подход Senior-разработчика: использование строки в качестве таблицы поиска

def letter_value(ltr: str) -> int return 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.index(ltr) + 1

Используя метод .index() для строки, мы получаем индекс, следовательно, letters.index('c')+1 приведёт к ожидаемому значению 3. Нет необходимости хранить значения в словаре, потому что индекс - это значение. Чтобы предотвратить +1, вы могли бы просто добавить пробел в начале строки, чтобы индекс a начинался с 1. Однако это зависит от того, хотите ли вы вернуть значение 0 для пробела или ошибки.

Как вы, возможно, уже подумали, да, мы также могли бы решить задачу "камень, ножницы, бумага", используя таблицу поиска:

def points_per_shape(shape: str) -> int: return 'XYZ'.index(shape) + 1

4. Усовершенствованные срезы

На 5-й день требуется прочитать буквы из строк (см. Ввод ниже). Каждая буква находится в четвертом индексе, начиная с индекса 1. Теперь практически каждый программист на Python будет знаком с разделением строк и списков, используя, например, list_[10:20]. Но чего многие люди не знают, так это того, что вы можете определить размер шага, используя, например, list_[10:20: 2], чтобы определить размер шага 2. На 5-й день (и во многих других ситуациях кодирования) это может сэкономить вам много излишне сложного кода:

# INPUT [D] [N] [C] [Z] [M] [P] # DESIRED OUTPUT [' D ', 'NC', 'ZMP']

Подход Junior-разработчика: двойной цикл for с диапазоном range и индексами

letters = [] with open('input.txt') as f: for line in f: row = '' for index in range(1, len(line), 4): row += line[index] letters.append(row)

Подход Senior-разработчика: использование продвинутых методов среза

with open('input.txt') as f: letters = [line[1::4] for line in f]

5. Использование атрибутов класса для хранения экземпляров класса

На одиннадцатый день описана ситуация, в которой обезьяны передают предметы друг другу. Для упрощения мы представим, что они просто передают бананы друг другу. Каждая обезьяна может быть представлена как экземпляр Python с id и количеством бананов в качестве атрибутов экземпляра. Однако обезьян много, и они должны уметь взаимодействовать друг с другом. Хитрость для хранения всех обезьян и для того, чтобы они могли взаимодействовать друг с другом, заключается в определении словаря со всеми экземплярами Monkey в качестве атрибута класса Monkey. Используя Monkey.monkeys[id], вы можете получить доступ ко всем существующим обезьянам без использования класса Monkeys или внешнего словаря:

class Monkey: monkeys: dict = dict() def __init__(self, id: int): self.id = id self.bananas = 3 Monkey.monkeys[id] = self def pass_banana(self, to_id: int): Monkey.monkeys[to_id].bananas += 1 self.bananas -= 1 Monkey(1) Monkey(2) Monkey.monkeys[1].pass_banana(to_id=2) print(Monkey.monkeys[1].bananas) 2 print(Monkey.monkeys[2].bananas) 4

Самодокументируемые выражения (БОНУС)

Этот трюк применим практически каждый раз, когда вы пишете программу на Python. Вместо определения в f-строке того, что вы печатаете (например, print(f"x = {x}"), вы можете использовать print(f"{x = }”) для печати значения со спецификацией того, что вы печатаете.

# INPUT x = 10 * 2 y = 3 * 7 max(x,y) # DESIRED OUTPUT x = 20 y = 21 max(x,y) = 21

Подход Junior-разработчика:

print(f"x = {x}") print(f"y = {y}") print(f"max(x,y) = {max(x,y)}")

Подход Senior-разработчика:

print(f"{x = }") print(f"{y = }") print(f"{max(x,y) = }")

Заключение

Мы рассмотрели 5 приёмов Python, которые отличают Сениоров от Джуниоров. Конечно, только применение этих приёмов не повысит кого-то внезапно до старшего разработчика. Однако, проанализировав разницу в стиле и шаблонах между ними, вы можете узнать разницу в том, как старший разработчик подходит к проблемам кодирования по сравнению с Джуниором, и вы можете начать усваивать эти подходы, чтобы в конечном итоге самому стать Senior-разработчиком!

Статья была взята из этого источника:

8484 показа
8585 открытий
Начать дискуссию