Zen of Python이 더 나은 코드 작성을 돕는 방법

Python 코드 작성을 더 잘하고 싶습니까? 다음은 Zen of Python이 첫 번째 단계를 수행하는 데 도움이 되는 방법입니다.

Python은 배우기가 매우 간단합니다. 그러나 유지하기 쉬운 관용적이고 파이썬적인 코드를 작성하는 것은 특히 초보 프로그래머에게 어려울 수 있습니다. PEP-20은 모범 사례를 준수하는 Python 코드 작성의 중요성을 설명하는 Tim Peters의 시 “The Zen of Python”을 소개했습니다.

Zen of Python을 읽으려면 Python REPL을 시작하고 다음을 실행할 수 있습니다.

>>> import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

보시다시피 Zen of Python의 대부분의 격언은 자명합니다. 일부 격언은 해석할 때 다음 격언과 결합되어야 하는 반면, 다른 격언은 이전 격언과 모순됩니다. 그럼에도 불구하고 Zen of Python은 재미있고 매력적이며 실용적입니다!

Zen of Python 해석하기

Zen of Python은 Python 프로그래밍을 위한 20가지 기본 원칙을 갖도록 제안되었습니다. 그러나 지금까지 격언은 19개뿐이다. 그것들을 살펴봅시다.

아름다운 것이 못생긴 것보다 낫습니다.

이 격언은 우아하고 Pythonic 코드를 작성하는 것의 중요성을 강조합니다.

다음 코드 조각에는 코드 냄새가 있습니다.

def square(num):
    squares = []
    for i in range(num):
        squares.append(i*i)
    return squares

함수:

  • 빈 목록을 초기화합니다.
  • 목록의 끝에 요소를 추가하고
  • 마지막으로 목록을 반환합니다.

이것은 기능적으로 올바르고(Pythonic이 아님) 유지 관리하기 어렵습니다.

생성기를 사용하면 훨씬 더 우아하게 작성할 수 있습니다. 다음은 위 함수와 동등한 생성기 함수입니다.

def square(num):
    for i in range(num):
        yield i*i

또는 더 좋은 방법은 다음과 같은 생성기 이해 표현을 가질 수 있다는 것입니다.

num = ...
squares = (i*i for i in range(num))

명시적인 것이 암시적인 것보다 낫습니다.

코드를 작성할 때 다른 개발자와 사용자가 코드의 암시적 또는 기본 동작을 추측하도록 내버려 두지 마십시오. 명시하십시오. 와일드카드 가져오기의 예를 들어보세요.

from some_module import * # wildcard import
from some_other_module import *

result = some_function() # where did this come from?

가능한 한 와일드카드 가져오기를 사용하지 마십시오. 명시적이고 비효율적이지 않기 때문입니다. 다른 모듈에서 함수와 클래스를 가져올 때는 구체적이어야 합니다.

from some_module import this_function # explicit import

result = this_function() # we now know.

단순한 것이 복잡한 것보다 낫습니다.

이 격언은 우리가 코드를 단순하게 유지하고 불필요한 복잡성을 피해야 한다고 말합니다. 예를 들어 문자열을 뒤집고 다음 재귀 솔루션을 구현할 수 있습니다.

def reverse_string(my_string):
  if my_string == "":
    return my_string
  else:
    return reverse_string(my_string[1:]) + my_string[:1]

이 방법이 효과가 있기는 하지만 더 간단하고 더 파이썬적인 방법이 있다는 점을 감안하면 이 문제에 대해 지나치게 엔지니어링된 해결책일 가능성이 높습니다.

문자열 슬라이싱 방식은 다음과 같습니다.

>>> rev_string = my_string[::-1]
>>> rev_string
'nohtyP'

내장 메소드와 함수를 사용하는 접근 방식은 다음과 같습니다.

>>> rev_string = ''.join(reversed(my_string))
>>> rev_string
'nohtyP'

복잡한 것이 복잡한 것보다 낫습니다.

그렇다면 Zen of Python의 다음 격언은 무엇을 전달합니까?

Python의 문자열 반전은 매우 간단한 작업입니다. 그러나 실제로는 더 복잡한 논리가 필요할 수 있습니다. 다음은 매우 간단한 예입니다.

데이터베이스에 연결해야 한다고 가정해 보겠습니다.

  • 데이터베이스의 구성 정보를 검색하려면 먼저 toml 구성 파일을 구문 분석해야 합니다.
  • 데이터베이스 커넥터를 설치해야 합니다.
  • 그런 다음 데이터베이스에 연결하고, 연결 오류를 예상하고, 오류 처리를 구현하는 등의 기능을 정의할 수 있습니다.
  • 마지막으로 데이터베이스에 연결한 후 쿼리할 수 있습니다.

이것은 여전히 ​​간단하지만 문자열 반전에 비해 더 복잡한 논리가 필요합니다. 그러나 이것이 복잡해야 한다는 의미는 아닙니다. 여전히 내장 모듈 코드의 기능을 효과적으로 사용하고 다른 개발자가 코드를 읽고 이해하고 기여할 수 있도록 코드를 구성할 수 있습니다.

플랫은 중첩보다 낫습니다.

플랫 구조는 중첩 구조보다 구문 분석하고 이해하기 쉽습니다. 프로젝트에서 작업할 때 별도의 모듈을 만들어 기능을 분리하고 싶은 유혹을 느낄 수 있습니다. 그러나 너무 많은 세분성은 과도할 수 있습니다.

즉, 평면 구조를 넘어서야 하는 경우가 종종 있습니다. 그러나 중첩이 필요하더라도 최소한으로 유지하십시오.

다음은 예입니다.

from db_info.config.actions.parse.parse_config import parse_toml # too difficult to parse!
...

from db_config.parse_config import parse_toml # much better!
...

희박한 것이 조밀한 것보다 낫습니다.

개발자 여정을 막 시작한 경우 언어의 일부 기능을 남용하고 싶은 유혹을 느낄 수 있습니다. 예를 들어 목록 내포는 Pythonic이지만 필요할 때 사용할 때만 가능합니다.

다음 이해를 보십시오.

prices_dict = {'melons':40,'apples':70,'berries':55}
items = [(fruit,price) for fruit in prices_dict.keys() if fruit.startswith('m') for price in prices_dict.values() if price < 50]
print(items)
# Output: [('melons', 40)]

목록 이해가 너무 조밀하고 구문 분석하기 어렵습니다. 이 경우 조건문과 동등한 for 루프를 사용하면 더 읽기 쉽습니다. 이해가 어렵다는 뜻입니다. 🙂

가독성이 중요합니다.

항상 읽기 쉬운 코드를 작성해야 합니다. 다음은 코드 가독성을 향상시키는 몇 가지 간단한 방법입니다.

  • 설명 변수 이름 사용
  • 함수 및 클래스에 독스트링 추가
  • 필요한 곳에 코드 주석 달기
  • 함수의 인수 및 반환 유형에 대한 유형 힌트 추가

특별한 경우는 규칙을 어길 만큼 특별하지 않습니다.

언어의 규칙과 권장 모범 사례를 가능한 한 많이 준수해야 합니다.

그러나 이것이 항상 가능합니까? 아니요, 그래서 다음 격언이 있습니다.

실용성이 순결을 능가하지만.

이것은 이전 격언의 연속입니다. 언어의 규칙을 따르는 것이 권장되지만 경우에 따라 일부 원칙을 따르지 않아도 괜찮습니다.

오류는 절대로 조용히 전달되어서는 안 됩니다.

Python 런타임 오류는 매우 일반적입니다. 좋은 방법은 항상 오류를 처리하고 빠른 수정으로 오류를 침묵시키지 않는 것입니다.

다양한 오류 유형에 대해 적절한 오류 처리를 예상하고 구현할 수 있습니다.

try:  
    # doing this
except ErrorType1:
    # do something
except ErrorType2:
    # do something else
...

베어 및 일반 예외를 피해야 합니다. 최신 버전의 Python(Python 3.11 이후)은 예외 체인 및 예외 그룹을 지원하여 보다 정교한 예외 처리를 수행합니다.

명시적으로 침묵하지 않는 한.

이것은 이전 격언을 따릅니다. 디자인이 오류를 침묵하도록 요구하거나 허용하는 경우 명시적으로 수행해야 합니다.

예: 데이터베이스에 연결할 때 잘못된 구성 정보로 인해 OperationalError가 발생할 수 있습니다. 사용자 지정 구성을 사용하여 연결해 보십시오. OperationalError가 있는 경우 기본 구성을 사용하고 데이터베이스에 연결을 시도합니다.

try:
   # connecting using custom config
except OperationalError:
   # connect using default config

모호함에 직면하여 추측하려는 유혹을 거부하십시오.

Zen of Python의 이 격언은 자명합니다. 의심스러울 때는 추측하지 마십시오. 그러나 코드를 실행하고 출력을 확인하십시오. 그런 다음 원하는 동작이 있는지 여부에 따라 가독성을 개선하거나 필요에 따라 논리를 수정합니다.

부울 튜플을 사용하여 다음과 같은 간단한 예를 살펴보십시오.

>>> True, True == (True, True)
(True, False)
>>> True, (True == (True, True))
(True, False)
>>> (True, True) == (True, True)
True

이를 수행할 수 있는 분명한 방법은 하나, 가급적이면 하나만 있어야 합니다.

특정 작업을 수행하려면 권장되는 파이썬 방법이 하나만 있어야 합니다. 그러나 모든 문제에 대해 여러 솔루션이 있을 수 있습니다.

간단한 문자열 반전 예제에서도 재귀 솔루션, 문자열 슬라이싱 및 join() 메서드를 살펴보았습니다.

이것은 엠-대시의 일관성 없는 사용을 감안할 때 내부 농담이기도 합니다. 우리는 일반적으로 선행 및 후행 공백 없이 전각 대시를 사용합니다. 또는 선행 및 후행 공백 모두와 함께 사용합니다.

그래서 우리가 추론할 수 있는 것은 이렇습니다. 일을 처리하는 Python 방식은 하나뿐이어야 한다고 강조하는 격언은 그 자체로 두 가지 이상의 방식으로 작성될 수 있습니다.

네덜란드인이 아닌 이상 처음에는 그 방법이 명확하지 않을 수 있습니다.

가벼운 메모로 작성되었으며, 이것은 Python의 창시자(네덜란드인)인 Guido Van Rossum을 나타냅니다. 특정 작업을 수행하는 (가장) Python적인 방법은 Python 작성자에게만 자연스럽게 제공됩니다.

따라서 개발자가 언어의 기능을 더 잘 활용하려면 경험과 경험을 통한 학습이 필요합니다.

지금이 그 어느 때보다 좋습니다.

Zen of Python의 다른 격언과 마찬가지로 이것도 몇 가지 다른 방식으로 해석될 수 있습니다.

한 가지 해석은 개발자로서 프로젝트 코딩 시작을 미루는 것이 매우 일반적이라는 것입니다. 프로젝트의 세부 사항을 계획하기 위해 기다리기보다는 지금 시작하는 것이 더 나은 생각입니다.

또 다른 가능한 해석은 다음과 같습니다. 유한한 수의 단계에서 실행되고 종료되는 코드는 종종 버그가 있고 무한 루프에 빠지는 코드보다 낫습니다.

결코 지금보다 더 나은 경우는 없습니다.

이 격언은 이전 격언과 모순되는 것 같습니다. 미루지 않는 것이 좋지만 문제를 충분히 생각하고 그에 따라 코드를 설계해야 합니다.

적절한 생각을 하지 않고 모듈을 코딩하는 것은 코드 냄새와 반패턴으로 가득한 나쁜 생각입니다. 이러한 코드는 수정 조치를 리팩토링하고 구현하기 어렵기 때문입니다.

구현이 설명하기 어렵다면 나쁜 생각입니다.

모든 논리는 아무리 복잡하더라도 항상 설명하기 쉽고 이해하기 쉬운 형태로 구현될 수 있습니다.

구현을 설명하기 어려운 경우 불필요한 복잡성이 있을 수 있습니다. 따라하기 쉽도록 코드를 수정하거나 리팩토링할 수 있습니다.

구현이 설명하기 쉽다면 좋은 생각일 수 있습니다.

이것은 이전 격언과 관련이 있으며 자명합니다. 구현을 간단한 용어로 설명할 수 있다면 좋은 생각일 가능성이 높습니다.

간단한 용어로 구현을 설명할 수 있는 코드는 최소한의 복잡성으로 읽기 쉽고 따라하기 쉬울 가능성이 매우 높기 때문입니다.

네임스페이스는 경적을 울리는 훌륭한 아이디어 중 하나입니다. 더 많은 작업을 수행해 봅시다!

Python에서 특정 범위의 개체는 네임스페이스의 이름을 사용하여 액세스할 수 있습니다. 예를 들어 클래스를 생성하고 이를 템플릿으로 사용하여 클래스의 인스턴스를 생성할 수 있습니다. 이제 인스턴스 변수는 모두 인스턴스의 네임스페이스에 있게 됩니다.

이를 통해 서로 다른 네임스페이스에 있으므로 충돌 없이 동일한 이름을 가진 객체를 사용할 수 있습니다. 그러나 필요한 경우에만 사용하고 코드의 단순성과 가독성이 손상되지 않도록 해야 합니다.

결론

이것이 이 튜토리얼의 전부입니다! 이 가이드가 Python의 Zen이 어떻게 Python의 코드 스타일과 좋은 코딩 관행을 강조하는지 이해하는 데 도움이 되었기를 바랍니다. 더 많이 코딩할수록 더 잘할 수 있습니다.

간결하고 읽기 쉬운 코드를 작성하는 방법에 관심이 있다면 Python one-liners에 대한 이 기사를 읽어보세요.