Игра Яндекс Практикума
Игра Яндекс Практикума
Игра Яндекс Практикума

5 ошибок Python-разработчиков, которые выдают новичка

Рассказали, какие основные ошибки делает начинающий Python-разработчик, как их можно избежать и на что в целом обращать внимание.

5К открытий22К показов

1. Неряшливость в коде

« from main.tasks import task »

Это не только код по PEP, сколько отсутствие видимой логики и структуры в коде.

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

По тексту я использую синтаксис Django ORM, так как он визуально проще, чем тот же SQL, и его можно представить как псевдокод для SQL.

Давайте понятные имена функции и переменным

Вместо c = count()

			count = count()
		

Вместо q = Model.objects.all()

			queryset = Model.objects.all()
		

Если будут большие связки из фильтров и прочего, можно

			qs = Modek.objects.all()
		

Вместо for x, y in _dict.items()

			for key, value in _dict.items()
		

Вместо count = User.objects.count()

			user_count = User.objects.count()
		

Кто-то решит, что лучше count_user, но я думаю, это индифферентно, главное, чтобы было понятно.

Курс по Frontend-разработке от Kata Academy
  • постоянный доступ
  • бесплатно
  • онлайн
tproger.ru

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

			def get_count_of_pikachu_charmander_squirtle_from_base():
    pickachu_count = Pokemon.objects.filter(type='pickachu').count()
    charmander_count = Pokemon.objects.filter(type='charmander').count()
    squirtle_count = Pokemon.objects.filter(type='squirtle').count()
    return pickachu_count + charmander_count + squirtle_count
		

может быть существенно упрощена несколькими способами.

Нормально:

			def get_pikachu_count():
    return Pokemon.objects.filter(type='pickachu').count()

def get_charmander_count():
    return Pokemon.objects.filter(type='charmander').count()

def get_squirtle_count():
    return Pokemon.objects.filter(type='squirtle').count()
		

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

Хорошо:

			def get_pokemon_count_by_type(_type):
    return Pokemon.objects.filter(type=_type).count()
		

Теперь функция не привязана к конкретным типам покемонов, мы можем переиспользовать ее много раз. Жаль только, что в случае, если нам нужно посчитать в целом несколько типов за раз.

Отлично:

			def get_pokemon_by_types(_type: (str, list)):
    if isinstance(_type, str):
        return Pokemon.objects.filter(type=_type).count()
    if isinstance(_type, list):
        return Pokemon.objects.filter(type__in=_type).count()
		

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

Поэтому я бы использовал окончательно вот такой вариант:

			def get_pokemon_by_types(_type: (str, list)) -> int:
    if not isinstance(_type, (str, list)):
        return
    if isinstance(_type, str):
        _type = [_type]
    return Pokemon.objects.filter(type__in=_type).count()
		

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

Если не получается вложить весь смысл в наименование функции или переменной, не пишите «x = » или «def func», а попробуйте разнести логику.

2. Отсутствие базовых знаний Linux/Unix

Наверняка в англоязычных зарубежных компаниях встречается разработка на Windows, но все же Python, как скриптовый язык, работает на Unix и Linux.

Оптимальная разработка проходит на Mac или Ubuntu (и других любимых дистрибутивах), с целью выложить код на сервер, который, скорее всего, будет на Ubuntu. Более того, в вакансиях начиная с мидла, а иногда с джуна, спрашивают базовые знания Linux. В идеале вы должны уметь использовать еще несколько утилит, которые помогут в работе или деплое, уметь создавать пользователей, группы, менять группы и права директориям.

Утилиты которые точно пригодятся в использовании

Supervisor

Позволяет запускать и отслеживать процессы. Например, чтобы при перезапуске системы у вас всегда запускался проект.

Cron

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

Nginx

Широко используемый веб-сервер. Если у вас веб-проект, без него практически не обойтись.

3. Слабые знания СУБД

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

Нереляционные СУБД

С нереляционными попроще, мы вот используем Redis, MongoDB и Clickhouse.

Redis — это key-value хранилище. Если провести аналогию с питоновскими типами — словарь, который отлично подходит для хранения кэша. Удобно использовать для межкомпонентного взаимодействия.

MongoDB — это хранилище JSON-документов, по такой же аналогии — список json’ов. Отлично подходит, если у вас где-то генерируется большое количество данных, которые вы не хотели бы терять. Например, если бы все эти данные писали в реальном времени в реляционную: это долго, могут случится ошибки записи, плюс нужна валидация и прочее. Mongo же пишет эти данные как есть, а в соседнем сервисе вы можете спокойно загрузить все данные в реляционную базу в своем темпе, пройдя все нужные валидации и проверки.

Clickhouse — можно сказать, новинка отечественных разработчиков, использует SQL язык, но при этом не является реляционной БД. Основная задача — хранение гигантского количества данных и возможность с ними работать. Собрать миллиард записей и посчитать сумму тех или иных записей будет быстрее, чем в обычной реляционной БД. Хорошо сжимает данные, можно разворачивать кластером, привычный SQL язык.

Подойдет, чтобы смотреть статистику, основанную на больших объемах данных.

Реляционные СУБД

Это, в первую очередь, MySQL и Postgres. Скорее всего, разработка на Python при участии БД будет проходить с помощью ORM, Django ORM или SQLAlchemy. Если это SQLAlchemy, это не так страшно, так как написание запросов в этой ORM так или иначе следует логике SQL. Django ORM максимально своеобразный.

Вот один и тот же способ получить имя пользователя и название его работы

  • в Django ORM:
			User.objects.values('user__name', 'user__job__name')
		
  • в SQLAlchemy:
			sql = select(User.name, Job.name).join(User.job)
session.execute(sql)
		
  • чистый SQL:
			SELECT users.name, jobs.name FROM users 
JOIN jobs ON jobs.id = users.job_id;
		

В «Алхимии» видно, что мы что-то все-таки присоединили, Django ORM же работает с другими абстракциями. Надо понимать, как работают запросы и чистый SQL. Например, в случае профилирования, если тормозит страница, и экран профилирования покажет некий запрос, который вызывается либо несколько раз, либо отъедает время загрузки. Тогда нужно этот запрос отыскать в коде. Опять же, если SQL из примера будет съедать 10 секунд загрузки, выше видно, как он будет выглядеть в «Алхимии» или в «Джанго ОРМ».

4. Забивание на чистоту GIT

Частая проблема, которая не просто запускается, но еще и поддерживается коллективом. У вас мастер-ветка выглядит как

«fix -> fix -> fix -> try -> featyre -> fix -> revert» ?

Самое популярное оправдание, которое я слышал на эту тему, мол, что привязался, работает же. Фактически да, так оно и есть, но код это не только про «работает же». Это еще и его сопровождение в будущем. Представьте, вас просят что-нибудь найти или пофиксить спустя кучу времени, а вы натыкаетесь на странный кусок кода. В истории GIT одно «feature-> feature -> feature»? И коллега, судя по аннотации GIT, даже работает, но тоже не может вспомнить, в каком бреду это было написано и по чьей просьбе.

Поэтому, как по мне, близкое к идеалу ведение коммитов:

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

Помимо самих коммитов, есть еще использование rebase и merge.

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

Начинаете работу над задачей? Делаете новую ветку от мастера (или как скажет тимлид).

Заканчиваете работу над задачей? Делайте финальный ребейз на мастер и посквошьте коммиты. Работаете с коллегой в одной ветке? Вместо мержа origin-ветки в локальную, делайте ребейз на ориджин-ветку. История коммитов будет чище, сложно будет в ней теряться.

5. Написание тестов

Я не буду про TDD. Это приходит с годами. Выдаст в вас новичка не только отсутствие тестов, так и написание тестов на каждый чих. Нужно сразу понять, где начинается бизнес-логика, и покрывать ее тестами.

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

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

Следите за новыми постами
Следите за новыми постами по любимым темам
5К открытий22К показов