Інформація про дані Платона.
Вертикальний пошук і штучний інтелект.

Код Pythonic: найкращі методи, щоб зробити ваш Python більш читабельним

Дата:

Усі ветерани Розробники Python (Pythonistas) проповідують про написання коду на Pythonic. Якщо ви хтось, хто витратив деякий час на написання коду Pythonic, ви натрапите на найкращі методи. Але що саме таке код Pythonic, і як слід пам’ятати про основні проблеми/уникати очевидних (поганих) практик?

На щастя, спільнота Python наділена відносно простим і повним набором рекомендацій щодо стилю коду та «Pythonic» ідіом. Це одна з основних причин високої читабельності коду Pythonic. Читабельність і спрощений синтаксис є основою Python.

У цій публікації ми поговоримо про кілька дуже важливих рекомендацій щодо стилю та ідіом Pythonic, а також про те, як поводитись із застарілим кодом.

Один оператор коду на рядок

Якщо ви пишете роз’єднані оператори в один рядок, ви порушуєте суть Python. Єдиним винятком є ​​розуміння списку та кілька інших складених операторів. Вони дозволені і прийняті за їхню стислість і виразність.

Погана практика

print 'foo'; print 'bar' if x == 1: print 'foo' if <complex comparison> and <other complex comparison>: # do something

Найкраща практика

print 'foo'
print 'bar' if x == 1: print 'foo' cond1 = <complex comparison>
cond2 = <other complex comparison>
if cond1 and cond2: # do something

Явний код

Найпростіший (і найлегший для розуміння) спосіб написання коду завжди найкращий.

Погана практика

def make_complex(*args): x, y = args return dict(**locals())

Наведений вище код повертає:
{'args': (1, 2), 'x': 1, 'y': 2}
Це зайве, оскільки нам потрібні лише x і y, тоді як він також повертає 'args'.
Крім того, якби ми мали:

def make_complex(*args): x,y = args z = x+y return dict(**locals())

Наведена вище функція також поверне 'z':3 у локальному dict. Найкраща практика — вказати, що потрібно, і дотримуватись найбільш прямого підходу. Пам’ятайте, що все повинно бути простим і чітким.
Ще одна помилка цієї поганої практики полягає в тому, що якщо ви передаєте більше 2 параметрів під час виклику функції: make_complex(1,2,3), це кидає a valueError подобається це:
Помилка коду Pythonic

Найкраща практика

def make_complex(x, y): return {'x': x, 'y': y}

Передача аргументів до функцій

Є чотири різні способи передачі аргументів функції:

  1. Позиційні аргументи: Це найпростіші форми аргументів. Позиційні аргументи повною мірою є частиною значення функції, і їх порядок буде таким, у якому вони визначені. Наприклад, в cal_area(length, breadth) or send_msg(message, recipient), розробнику не доведеться турбуватися про те, що ці дві функції вимагають 2 аргументи або їх порядок.

примітки: У наведених вище двох прикладах ви також можете викликати функції з різними порядками, використовуючи такі ключові слова, як: cal_area(breadth = 40.0, length=90) or send_msg(recipient='Mak', message='Hello there!').

  1. Аргументи ключових слів: Також відомий як kwargs, вони часто використовуються як додаткові параметри, що передаються функції. Коли функція має більше двох-трьох позиційних параметрів, її підпис стає важко запам’ятати.
    Kwargs корисні зі значенням за замовчуванням. Наприклад, кращий спосіб написання a send_msg функція буде такою: send_message(message, recipient, cc=None, bcc=None). Ось, cc та bcc є необов’язковими, і повертатимуться як «Немає», якщо значення не передане.

  2. Список довільних аргументів: Якщо бізнес-логіка функції вимагає розширюваної кількості позиційних аргументів, її можна визначити за допомогою *args конструкції. Всередині функції, args буде кортежом усіх решти позиційних аргументів. Наприклад, send_msg(message, *args) можна викликати з кожним одержувачем як аргумент:
    send_msg('Hello there!', 'God', 'Mom', 'Cthulhu'), і область дії функції матиме args дорівнює ('God', 'Mom', 'Cthulhu').

  3. Словник аргументів довільних ключових слів: Якщо вашій функції потрібна невизначена серія іменованих аргументів, можна використовувати **kwargs побудувати. У тілі функції kwargs буде словником усіх переданих іменованих аргументів, які не були перехоплені іншими аргументами ключового слова в сигнатурі функції.
    використання *args, Python передає функції неключовий аргумент змінної довжини, але що, якщо ми хочемо передати аргумент ключового слова? Використання **kwargs, ми можемо передати змінну довжину аргументів ключового слова до функції.
    Наприклад, для функції нижче:

def introduction(**data): print("nData type of argument:",type(data)) for key, value in data.items(): print("{} is {}".format(key,value))
introduction(Firstname="Sita", Lastname="Sharma", Age=22, Phone=1234567890)
introduction(Firstname="John", Lastname="Wood", Email="[email protected]", Country="Wakanda", Age=25, Phone=9876543210)

Вихідний сигнал буде:

Data type of argument: <class 'dict'>
Firstname is Sita
Lastname is Sharma
Age is 22
Phone is 1234567890 Data type of argument: <class 'dict'>
Firstname is John
Lastname is Wood
Email is [email protected]
Country is Wakanda
Age is 25
Phone is 9876543210

примітки: Необхідна така ж обережність, як і для списків довільних аргументів. Причини схожі: ці потужні методи можна використовувати лише тоді, коли є доведена необхідність, і не повинні використовуватися, якщо більш проста і зрозуміла конструкція є достатньою для вираження наміру функції.

Якщо розумно дотримуватись керівництва зі стилю кодування, ваші функції Python будуть такими:

  • легко читається (назва та аргументи не потребують пояснень)
  • легко змінити (додавання нового аргументу ключового слова не порушує інші частини коду)

Заяви про повернення

У міру того як функція зростає в складності, вона стає сприйнятливою до наявності кількох операторів повернення всередині тіла функції. Однак, щоб зберегти чіткий намір і стабільний рівень читабельності, бажано уникати повернення значущих значень у кількох вихідних точках тіла функції.

Наприклад, подивіться на приклад нижче (пояснюється вбудованими коментарями) про те, як уникнути додавання кількох точок виводу та натомість створювати винятки:

Погана практика

def complex_function(a, b, c): if not a: return None if not b: return None # Some complex code trying to compute x from a, b and c if x: return x if not x: # Some Plan-B computation of x return x

Найкраща практика

def complex_function(a, b, c): if not a or not b or not c: raise ValueError("The args can't be None") # Raising an exception is better # Some complex code trying to compute x from a, b and c # Resist temptation to return x if succeeded if not x: # Some Plan-B computation of x return x # One single exit point for the returned value x will help when maintaining the code.

Написання ідіоматичного Python

Ідіома – це фраза, яка не має буквального сенсу, але має сенс, коли ви познайомитеся з культурою, в якій вона виникла. Ідіоми програмування нічим не відрізняються. Це маленькі речі, які ви робите щодня на певній мові програмування або парадигмі, які мають сенс лише для людини, знайомої з її культурою.

Початківці Python можуть не знати про написання ідіоматичного Python, тому ми перерахували деякі поширені ідіоми Python:

Розпакування

Якщо ви хочете призначити імена або посилання на елементи списку під час його розпакування, спробуйте використати enumerate():

for index, item in enumerate(some_list): # do something with index and item

Ви можете використовувати змінні підкачки:

a, b = b, a

Вкладене розпакування також працює:

a, (b, c) = 1, (2, 3)

У Python 3, Пеп 3132 представив новий метод розширеного розпакування:

a, *rest = [1, 2, 3]
# a = 1, rest = [2, 3]
a, *middle, c = [1, 2, 3, 4]
# a = 1, middle = [2, 3], c = 4

Створення одноразових змінних

Якщо вам потрібно щось призначити (наприклад, під час розпакування), але ця змінна вам не потрібна, використовуйте __:

filename = 'foobar.txt'
basename, __, ext = filename.rpartition('.')

примітки:
Багато посібників зі стилів Python рекомендують використовувати єдине підкреслення _ для вихідних змінних, а не подвійного підкреслення __ рекомендовано тут. Питання в тому _ зазвичай використовується як псевдонім для gettext() функція, а також використовується в інтерактивному підказці для збереження значення останньої операції.

Використання замість цього подвійного підкреслення настільки ж зрозуміло і майже так само зручно. Перевага цієї практики полягає в усуненні ризику випадкового втручання в будь-який з цих інших випадків використання.

Створіть той самий список довжини N

Використовуйте список Python * оператор для створення простих списків і вкладених списків:

nones = [None]*4
foures_of_fours = [[4]]*5

вихід:
[Немає, Жодного, Жодного, Жодного]
[[4], [4], [4], [4], [4]]

Пошук елемента в колекції

Іноді нам потрібно шукати в колекції. Давайте розглянемо два варіанти: списки і набори. Візьмемо для прикладу наступний код:

def in_test(iterable): for i in range(1000): if i in iterable: pass
from timeit import timeit
timeit( "in_test(iterable)", setup="from __main__ import in_test; iterable = set(range(1000))", number=10000) Output: 0.5591847896575928 timeit( "in_test(iterable)", setup="from __main__ import in_test; iterable = list(range(1000))", number=10000) Output: 50.18339991569519 timeit( "in_test(iterable)", setup="from __main__ import in_test; iterable = tuple(range(1000))", number=10000) Output: 51.597304821014404

Обидві функції виглядають однаково, оскільки lookup_set() використовує той факт, що набори в Python є хеш-таблицями. Однак результати пошуку для цих двох є різними — тобто набори використовують O(log n), тоді як список має часову складність O(n).

Щоб визначити, чи є елемент у списку, Python повинен буде переглянути кожен елемент, доки не знайде відповідний елемент. Це займає багато часу, особливо для довгих списків. У наборі, з іншого боку, хеш елемента вкаже Python, де в наборі шукати відповідний елемент. В результаті пошук можна виконати швидко, навіть якщо набір великий.

Через ці відмінності в продуктивності часто доцільно використовувати набори або словники замість списків у випадках, коли:

  • колекція буде містити велику кількість предметів
  • ви будете неодноразово шукати предмети в колекції
  • у вас немає повторюваних елементів

Доступ до елемента словника

Не використовуйте метод dict.has_key(). Замість цього використовуйте x in d, або передайте аргумент за замовчуванням до dict.get(), оскільки він є більш пітонічним і видалено в Python 3.x.

примітки: Python2 збирається припинити роботу в 2020 році. Радимо використовувати Python 3.x для будь-якої розробки, оскільки більшість пакетів Python мають/припинять випуск оновлень для Python 2.x. Детальніше тут.

Погана практика

d = {'foo': 'bar'}
if d.has_key('foo'): print d['foo'] # prints 'bar'
else: print 'default_value'

Найкраща практика

d = {'foo': 'bar'} print d.get('foo', 'default_value') # prints 'bar'
print d.get('thingy', 'default_value') # prints 'default_value' # alternative
if 'hello' in d: print d['foo']

Фільтрування списку

Ніколи не видаляйте елементи зі списку під час його повторення. Чому? Якщо доступ до вашого списку здійснюється за допомогою кількох посилань, той факт, що ви просто перевстановлюєте одне з посилань (а НЕ змінюєте сам об’єкт списку), може призвести до незначних, катастрофічних помилок. Детальніше про це читайте тут.

Погана практика

# Filter elements greater than 4
num_list = [1, 2, 3]
for i in num_list: if i > 2: num_list.remove(i)

Не робіть кілька проходів через список.

while i in num_list: num_list.remove(i)

Найкраща практика

Використовуйте вираз для розуміння списку або генератора:

# comprehensions create a new list object
filtered_values = [value for value in sequence if value != x] # generators don't create another list
filtered_values = (value for value in sequence if value != x)

Оновлення значень у списку

Пам’ятайте, що присвоєння ніколи не створює новий об’єкт. Якщо дві або більше змінних посилаються на той самий список, зміна однієї з них змінює їх усіх.

Погана практика

# Add three to all list members.
a = [3, 4, 5]
b = a # a and b refer to the same list object for i in range(len(a)): a[i] += 3 # b[i] also changes

Найкраща практика

a = [3, 4, 5]
b = a # assign the variable "a" to a new list without changing "b"
a = [i + 3 for i in a]
b = a[:] # even better way to copy a list

Використовувати with open синтаксис для читання з файлів. Це автоматично закриє файли для вас.

Погана практика

f = open('file.txt')
a = f.read()
print a
f.close()

Найкраща практика

with open('file.txt') as f: for line in f: print line

Команда with метод кращий, оскільки він гарантує, що ви завжди закриєте файл, навіть якщо всередині блоку виникає виняток.

Робота з успадкованим кодом

Ми розглянули основи написання хорошого коду на Python. Тепер варто подивитися на мистецтво роботи з великими проектами на Python. Як ви можете взятися за нові проекти з відкритим чи закритим кодом? Які кроки потрібно виконати для реорганізації застарілого коду? Які найкращі методи, щоб навчитися працювати над новим проектом?

Часто, коли ви приєднуєтеся до нової організації, ви отримуєте кодову базу для розуміння та реорганізації, або вам потрібно використовувати застарілий код для реорганізації. Іноді завдяки цій ситуації ви опинитеся в глибокому стражданні і не зможете зрозуміти, з чого починається.

На цьому етапі важливо визначити «застарілий код/проект», щоб ми всі були на одній сторінці. Ось що ви зустрінете:

  • «старший» проект, який існує завжди
  • кодова база без будь-яких тестів
  • проект, над яким ніхто не хоче працювати
  • «Всі, хто працював над цим, залишили компанію багато років тому…»

Все вищесказане до певної міри правильно, але іноді проекти виконуються поспіхом і запускають у виробництво, перш ніж усі зрозуміють, що є багато можливостей для вдосконалення. Отже, як ми будемо боротися зі застарілим проектом?

Нижче наведено короткий список кроків, яких ви повинні виконати, щоб зробити ваш шлях рефакторингу простішим і плавнішим:

  1. Перш за все, переконайтеся, що проект знаходиться в системі контролю версій.
  2. Видалити прокоментований код. (Після запуску проекту завжди видаляйте прокоментований код.)
  3. Запустити тести/додати тести. Переконайтеся, що ви маєте принаймні 80% охоплення тестом. Використовуйте pytest або подібні пакети Python для відстеження тестового покриття.
  4. Скористайтесь Пілінт/Стервятник. Завжди думайте про те, щоб запустити якийсь тип підводка над кодом, щоб побачити, наскільки він «здоровий». Спробуй шукати:
    • Невикористані змінні
    • Все, що відзначається як потенційна помилка
  5. Використовуйте засоби форматування, такі як Flake8 або PEP8. Ці рекомендації можна використовувати для переформатування коду Python, щоб зробити його більшим PEP8 скарга.
  6. Напишіть більш ідіоматичний Python (як описано вище).

Висновок

Завдяки зростаючому співтовариству Python і початківцям Pythonista ми маємо Python майже у всіх галузях розробки, таких як наука про дані, веб-розробка, мобільна розробка та AI тощо. Тому стає все важливішим переконатися, що ми завжди поставляємо код корпоративного рівня дотримуючись відповідних рекомендацій.

Завдяки цим основним інструментам — і красі самої мови Python — створення чудового коду та продуктів не повинно бути страшною пропозицією. Тепер, коли ви пройшли через ці вказівки, спробуйте їх проект Python з відкритим кодом!

Щоб отримати додаткові поради з Python, перегляньте ці публікації:

Джерело: https://www.codementor.io/blog/pythonic-code-6yxqdoktzt

spot_img

Остання розвідка

spot_img

Зв'яжіться з нами!

Привіт! Чим я можу вам допомогти?