파이썬 코딩 가이드라인

코전트 프로젝트를 위하여 롭 나이트(Rob Knight)가 1/28/07에 작성함
한글판 johnsonj 2008.06.06 금

목차

왜 코딩 지침이 있어야 하는가?

변수를 어떻게 불러야 하는가?

이름짓기 관례란 무엇인가?

모듈(소스 파일)을 어떻게 조직할까?

주석은 어떻게 써야 하는가?

코드를 어떤 형식으로 작성해야 하는가?

유닛 테스트는 어떻게 작성해야 하는가?

사용해야 할 편리한 파이썬 관용구가 있는가? (따로 문서로 다룸)

왜 코딩 지침이 있는가?

프로젝트 크기가 커질 수록, 일관성이 점점 더 중요해진다. 이 프로젝트는 과업을 따로따로 시작한다. 그러나, 각 과업이 성숙할 수록 공유 라이브러리 안으로 통합되어 들어 갈 것이다. 신뢰성 있는 코드를 통합하려면 유닛 테스트와 일관성 있는 스타일이 중요하다. 또한, 이름과 인터페이스에 관하여 추측하는 것만으로도 보통 올바른 판단이 되어야 한다.

좋은 코드를 곁에 두는 것이 쓸모가 있다. 이렇게 표준을 지킨 코드는 교육 목적에 유용하며, 또 인터뷰 시간에 잠재적인 고용인에게 보여주기 위해서도 쓸모가 있다. 대부분의 사람들은 코드 샘플을 보여주기를 별로 달가워하지 않는다: 좋은 코드를 작성하고 검증해 두었다면 누구보다도 앞서 나갈 것이다. 또한, 재사용가능한 구성요소라면 훨씬 더 쉽게 요구에 적응하고 새로운 분석을 수행할 수 있을 것이다.

변수를 어떻게 불러야 하는가?

사람들이 보통 짐작할 만한 이름을 선택하자. 설명적으로 이름을 짓되, 너무 길게 짓지는 말자: ccurr 또는 current_genbank_record_from_database보다 curr_record가 더 좋다. 코딩 지침이 있는 부분적인 이유는 누구든지 더 쉽게 같은 방식으로 추측할 수 있기 때문이다. 누가 알겠는가: 몇 달이 지나면, 그렇게 추측하고 있는 사람이 바로 여러분 자신이 될 수도 있는 것이다.

좋은 이름은 찾기가 쉽지 않다. 다른 사람도 사용하고 있는 인터페이스가 아닌 한, 두려워하지 말고 이름을 바꾸자. 코드 작업을 하면서 조금 시간이 걸려야 제대로 합리적인 이름을 지을 수 있을 것이다: 유닛 테스트가 있다면, 바꾸기가 쉽다. 특히 전역적인 검색과 치환으로 말이다.

개별적인 것들에는 단수형 이름을 사용하고, 집단에는 복수형 이름을 사용하자. 예를 들어, self.Name을 보면 무엇인가 단수형 문자열이라는 것을 짐작하지만, self.Names이라면 무엇인가 리스트나 사전 같이 회돌이할 수 있는 것이라고 추측할 수 있다. 가끔은 그 판단이 곤란한 경우도 있을 수 있다: self.Index는 위치를 보유한 정수인가, 아니면 검색을 용이하기 위해 이름이 키인 레코드를 보유한 사전인가? 이런 것들이 잘 이해가 되지 않는다면, 그 이름은 아마도 문제를 피하기 위하여 바꾸어야 할 것이다: self.Position이나 self.LookUp을 시도해 보자.

이름에 유형 부분을 넣지 말자. 구현을 나중에 바꾸고 싶을 수도 있기 때문이다.RecordDictRecordList 또는 등등 보다는 Records를 사용하자. 헝가리식 표기법도 사용하지 말자 (즉, 거기에서는 이름 앞에 유형을 표기한다).

가능하면 정확하게 이름을 짓자. 변수가 입력 파일의 이름이라면, infile_name이라고 불러야지, input이나 file이라고 부르면 안되며 (어쨋거나 사용해서 안되는데, 그 이름이 키워드이기 때문이다), infile이라고 해서도 안된다 (단지 그의 이름이 아니라, 마치 파일 객체처럼 보이기 때문이다).

메쏘드나 함수가 돌려주는 값을 저장하려면 result를 사용하자. 더 설명이 그럴듯한 이름이 없는 한 함수나 메쏘드가 임의의 데이터를 처리하는 경우에 입력에는 (예를 들어, 연속열 데이터나 숫자 리스트, 등등에는) data를 사용하자.

한글자-짜리 변수 이름은 수학 함수나 제한된 범위 안에 있는 회돌이 반복자와 같은 곳에서만 나타나야 한다. 제한된 범위에는 for k in keys: print k와 같은 것이 있는데, 여기에서 k는 오직 한 두 줄에서만 생존한다. 회돌이 반복자는 자신의 회돌이 안에 있는 변수를 참조 한다: for k in keys, i in items, 또는 for key in keys, item in items. 회돌이가 길거나 같은 범위 안에서 한글자-짜리 변수가 여럿이라면, 이름을 바꾸자.

약자는 제한적으로 사용하자. 유명한 약자라면 괜찮지만, 6개월 후에 다시 코드에 돌아와서 sptxck2가 무슨 뜻인지 추측하고 싶지는 않을 것이다. 시간을 더 들여서 species_taxon_check_2라고 타자할 가치는 있지만, 여전히 바람직하지 못한 이름이다: 어떤게 제일 좋은가? taxon_is_species_rank와 같이 더 이상 설명이 필요없는 것이 훨씬 더 좋다. 특히, 그 변수가 오직 한 두 번 사용된다면 말이다.

허용가능한 약자들. 다음은 많이 알려져 있고 문제없이 사용된다:

완전한 이름 축약형
alignment aln
archaeal arch
auxillary aux
bacterial bact
citation cite
current curr
database db
dictionary dict
directory dir
end of file eof
eukaryotic euk
frequency freq
expected exp
index idx
input in
maximum max
minimum min
mitochondrial mt
number num
observed obs
original orig
output out
phylogeny phylo
previous prev
protein prot
record rec
reference ref
sequence seq
standard deviation stdev
statistics stats
string str
structure struct
temporary temp
taxonomic tax
variance var

언제나 from module import Name, Name2, Name3... 구문을 사용하자. import module 또는 from module import * 대신에 말이다. 이편이 더 효율적이며, 이후 코드에서 타자수를 줄여주며, 훨씬 더 쉽게 이름 충돌을 보고 구현을 교체할 수 있다.

이름짓기 관례란 무엇인가?

이름 짓기 관례 요약
유형 관례 예제
함수 action_with_underscores find_all
변수 noun_with_underscores curr_index
상수 NOUN_ALL_CAPS ALLOWED_RNA_PAIRS
클래스 MixedCaseNoun RnaSequence
공개 특성 MixedCaseNoun IsPaired
비밀 속성 _noun_with_leading_underscore _is_updated
공개 메쏘드 mixedCaseExceptFirstWordVerb stripDegenerate
비밀 메쏘드 _verb_with_leading_underscore _check_if_paired
1급 비밀 데이터 __two_leading_underscores __delegator_object_ref
특성에 짝지어진 매개변수 SameAsProperty def __init__(data, Alphabet=None)
공장 함수 MixedCase InverseDict
모듈 lowercase_with_underscores unit_test
전역 변수 gMixedCaseWithLeadingG 예제 없음 - 이런 것은 비추천!

이름짓기 관례를 따르는 것이 중요하다. 이름이 가르키는 것을 짐작하기가 훨씬 더 쉽기 때문이다. 특히, 이름이 어떤 영역 안에 정의되어 있는지, 무엇을 가리키는지, 값을 바꾸어도 괜찮은지, 그리고 가리키는 것을 호출할 수 있는지 쉽게 추측할 수있다. 다음 규칙대로 구분한다.

lowercase_with_underscores는 (함수/메쏘드 매개변수를 포함하여) 모듈내부 변수에 사용. 예외: __init__에서는 어떤 매개변수든지 그 객체의 특성을 초기화하는데 사용되는 것이라면 그 특성과 정확하게 대소문자를 비롯하여, 철자가 같아야 한다. 이렇게 하면 그 정확한 필드 이름을 가진 사전을 **kwargs로 사용하여 데이터를 손쉽게 초기화할 수 있다.

MixedCase는 형태는 클래스공개 특성 그리고 공장 함수가 마치 클래스에 대하여 추가 구성자처럼 행위할 때 사용한다.

mixedCaseExceptFirstWord공개 메쏘드와 함수에 사용한다.

_lowercase_with_leading_underscore의 형태는 사적인 함수와 메쏘드 그리고 특성에 사용한다.

__lowercase_with_two_leading_underscores사적 특성과 함수가 하부클래스에 의하여 덮여 씌여지면 안된다.

이름붙은 상수에는 CAPS_WITH_UNDERSCORES의 형태로 짓는다.

gMixedCase는 (즉, 'g'가 앞에 붙은 혼합형은) 전역변수(globals)에 쓴다. 전역변수는 절대적으로 적게 그리고 주의해서 사용하여야 한다. 싱글톤 패턴이나 그런 비슷한 시스템에 슬쩍 끼워 넣는다 할지라도 말이다.

단어를 함께 읽어서 문제가 없다면 밑줄문자는 생략할 수 있다. in_file이나 out_file로 쓰기 보다infileoutfile이 좋다; 그러므로 infilename이나 outfilename 그리고 in_file_nameout_file_name (너무 길어서 쉽게 읽을 수 없음) 보다는 infile_nameoutfile_name이 더 좋다.

어떻게 모듈(소스 파일)을 조직해야 하는가?

각 파일에서 첫 줄은 #!/usr/bin/env python가 되어야 한다. 이렇게 해야 파이썬을 묵시적으로 불러서 예를 들어, CGI 상황에서 파일을 스크립트로 실행시킬 수 있다.

다음에는 설명과 개정 이력이 있는 문서화문자열이 있어야 한다. 설명이 길면, 첫 한 줄에 자신을 짧게 요약하고 나머지는 다음 한 줄을 띄우고 기술한다. 개정 이력에는 모듈이 처음 작성된 때가 기록되어 있어야 하고, 인터페이스와 알고리즘에 변화가 있으면 문서화되어야 한다. CVS로 옮겨가더라도, 이 정보가 문서화문자열로부터 문서를 생산하는 자동화 도구에 전해지는 것이 유용하다.

반입 서술문을 비롯하여 모든 코드는 반드시 문서화 문자열이 따라야 한다. 그렇지 않으면, 파이썬이 문서화문자열을 인식할 수 없고, 따라서 상호대화 세션에서 (즉, obj.__doc__를 통하여) 또는 자동화 도구로 문서를 만들어 낼 때 그 정보에 접근하지 못한다.

내장 모듈을 먼저 반입한 다음, 제 삼자 모듈을 반입하고, 그 다음에 여러분의 모듈이 있는 경로를 반입한다. 특히, 여러분의 모듈이 있는 경로와 이름을 더하게 되면 급속하게 변화할 가능성이 높다: 한 곳에 모아 두는 것이 찾기가 더 쉽다.

모듈 구조의 예:

#!/usr/bin/env python
"""Provides NumberList and FrequencyDistribution, classes for statistics.

NumberList holds a sequence of numbers, and defines several statistical
operations (mean, stdev, etc.) FrequencyDistribution holds a mapping from
items (not necessarily numbers) to counts, and defines operations such as
Shannon entropy and frequency normalization.
"""

from math import sqrt, log, e
from random import choice, random
from Utils import indices

class NumberList(list):
    pass    # 이하 코드 생략
class FrequencyDistribution(dict):
    pass    # 이하 코드 생략

if __name__ == '__main__':    # 명령-줄로부터 호출되면 실행되는 코드
    pass    # 아무것도 하지 않는다 - 코드 생략
# 이 구조를 모듈을 사용하는 법을 보여주는 간단한 예로 사용하시거나,
# 또는 모듈을 정말로 스크립트로 호출하려면.
		

어떻게 주석을 작성해야 하는가?

코드가 변하면 항상 주석을 갱신하자. 올바르지 않은 주석은 차라리 없는 것보다 못하다. 그 때문에 엉뚱한 길로 갈 수 있다.

주석은 코드 그 자체보다 더 많은 정보를 알려주어야 한다. 주석을 주의깊게 살펴보자: 보시면 코드를 다시 작성하는 것이 더 좋음을 알 것이다 (특히, 변수 이름을 다시 짓고 주석을 제거하는 것이 좋다.) 특히, 나중에 코드에서 설명이 필요한 묘한 숫자들과 기타 상수를 흩뿌리지 말자. 이름 그 자체로 문서가 되는 변수를 사용하는 것이 훨씬 더 좋다. 특히 같은 상수를 한 번 이상 사용한다면 말이다. 또한, 상수를 클래스나 실체 데이터로 집어 넣는 것을 고려해보자. 왜냐하면 '상수'는 자주 변경되며 여러 메쏘드에서 필요하기 때문이다.

잘못: win_size -= 20        # win_size를 20만큼 줄인다
좋음:    win_size -= 20        # 스크롤 바에 여백을 남긴다
올바름: self._scroll_bar_size = 20
              win_size -= self._scroll_bar_size
		

코드 블럭 안에서는 문자열이 아니라, #로 시작하는 주석을 사용하자. 파이썬은 진짜 주석을 무시하지만, 문자열에 대해서는 저장 공간을 할당해야 하기 때문이다 (내부 회돌이 안에서 수행성능에 악영향을 미칠 수 있다.).

메쏘드와 클래스 그리고 함수는 삼중 겹 따옴표(""")를 사용한 문서화 문자열로 시작하자. 문서화 문자열은 그 자체로 의미가 있는 한-줄짜리 설명으로 시작해야 한다 (자동화된 포맷 도구들이 많이 있고 IDE가 있으므로, 이를 이용하자). 이 다음에는 빈 줄 하나가 오고, 다음에 (혹 있다면) 매개변수에 대한 설명이 따라온다. 마지막으로, 좀 더 자세한 정보를 추가하자. 예를 들어 좀 더 길게 기술하고, 알고리즘에 관한 주의사항이라든가, 상세하게 매개변수에 관하여 지적하든가, 등등. 사용 사례가 있다면, 마지막에 나타나야 한다. 매개변수에 대한 기술은 철자와 대소문자 등등이 정확해야 한다.

예를 들어:

def __init__(self, data, Name='', Alphabet=None):
    """Returns new Sequence object with specified data, Name, Alphabet.

    data: The sequence data. Should be a sequence of characters.

    Name: Arbitrary label for the sequence. Should be string-like.

    Alphabet: Set of allowed characters. Should support 'for x in y'
    syntax. None by default.

    Note: if Alphabet is None, performs no validation.
    """
		

코드가 변하면 항상 문서화 문자열을 갱신하자. 유효 기한이 지난 주석처럼, 문서화문자열도 기한이 지나면 시간이 낭비된다.

코드의 모양은 어떻게 만들어야 하는가?

들여쓰기에는 공백 4개를 사용하자. 탭을 사용하지 말자 (편집기에서는 탭을 공백으로 바꾸도록 설정해두자). 탭의 행위는 플랫폼마다 다르며, 구문 에러를 야기한다. 오늘날까지도 많은 사람이 곤경을 겪고 있다.

줄은 79 문자를 넘기지 말자. 어떤 편집기에서는 긴 줄이 불편하기도 하고, 긴 줄은 인쇄할 때 깨어지면 혼란스러우며, 코드 조각을 이메일로 보내기도 어렵게 된다 (특히 이메일 클라이언트나 수신자가 '고맙게도' 줄을 자동으로 접도록 해 두 었다면 말이다.). 줄을 연속하려면 \을 사용하자. \ 다음에 공백을 두면 안되니 주의하자.

빈 줄은 클래스 정의와 메쏘드 정의를 돋보이게 하는데 사용되어야 한다. 클래스 정의는 두 개의 빈줄로 나누자. 메쏘드는 한 개의 빈줄로 가르자.

연산자 주위에는 공백을 일관성있게 사용하자. 일관성없는 공백 때문에 함께 그룹지어져 있는 것들을 한 눈에 보기가 어려울 수 있다.

좋음:  ((a+b)*(c+d))
가능:    ((a + b) * (c + d))
나쁨:   ( (a+ b)  *(c +d  ))
		

가름자 뒤에 또는 조각썰기 가름자 안에 공백을 사용하지 말자. 여기에 공백이 있으면 연관된 것들을 알아보기가 어렵다.

좋음: (a+b), d[k]
나쁨:  ( a+b ), d [k], d[ k]
		

유닛 테스트는 어떻게 작성해야 하는가?

한줄 한줄 모두 검증되어야 한다. 과학적인 작업에서, 버그는 실제로 여러분이 절대로 만나지 않을 단순히 불만스런 사용자만을 뜻하지 않는다: 버그는 논문 철회를 뜻하며 경력에 오점을 남긴다. 코드에서 산출된 결과로 결론을 내리기 전에 코드를 먼저 완전히 검증하여야 한다.

검증은 바람직한 인터페이스를 고안할 기회이다. 메쏘드를 위하여 테스트를 작성한 후에 메쏘드를 작성하자: 종종, 이렇게 하면 어떻게 메쏘드를 불러야 할지 어떤 매개변수를 취해야 할지 알아내는데 도움이 된다. 테스트를 이야기로 생각하자. 어떤 인터페이스의 모습을 원하는지 이야기하자. 한 번에 여러 메쏘드에 대하여 테스트를 작성하고, 인터페이스에 대하여 생각이 바뀜에 따라 바꾸어도 좋다. 그렇지만 다른 사람에게 인터페이스의 모습을 알려준 다음에는 절대로 바꾸면 안된다.

프로토타입을 생산 코드처럼 다루지 말자. 시도해볼 요량으로 테스트 없이 프로토타입 코드를 작성하는 것은 괜찮지만, 인터페이스와 알고리즘을 알아냈다면 테스트로 다시 작성해야 완성되었다고 생각할 수 있다. 종종, 이렇게 하면 어떤 인터페이스와 기능이 정말 필요한지 무엇을 제거해야 할지 결정하는데 도움이 된다.

한 번에 조금씩 작성하자. 생산 코드를 위하여, 두 개의 테스트와 두 개의 메쏘드를 작성하고, 다음 테스트 두개와 메쏘드 두개를 더 작성한다. 그리고 나서 이름을 바꾸든가 아니면 기능을 일반화시키자. 엄청나게 많이 남아 있는 코드가 '오로지 테스트를 작성해야 하는 것이라면' 90%가 완성된 것이 아니라 30%가 완성된 것이다. 광범위하게 테스트하면 디버깅 시간이 줄어든다. 왜냐하면 잘못된 것은 가장 마지막으로 작성한 테스트 모듬에 들어 있기 때문이다.

무엇이든 바꿨으면 검증 모듬(test suite)을 수행하자. 변화가 비록 하찮게 보일지라도, 검증을 수행하는데 겨우 2초면 되고 그러면 확신을 가질 수 있을 것이다. 이렇게 하면 지루하고 짜증스러운 디버깅 세션에 들어가지 않아도 된다. 아주 오래 전에 만들어져 있었던 그 변화가 당시에는 별로 중요해 보이지 않을 수 있다.

각 모듈에 대하여 따로 파일을 만들어서 유닛테스트 작업틀을 사용하자. 그 테스트 파일은 이름을 test_module_name.py로 짓자. 코드와 별도로 테스트를 유지하면 코드가 작동하지 않더라도 테스트를 바꾸고 싶은 유혹을 떨칠 수 있으며, 완전히 새롭게 구현하더라도 예전과 똑같은 (똑같이 행동하는) 인터페이스를 보여준다고 쉽게 검증할 수 있다.

부동 소수점 수나 순열로 무슨 일을 하고 있다면 evo.unit_test를 사용하자 (assertFloatEqual를 사용하자). 깔끔함에 가치를 두고 있다면 assertEqual를 사용하여 부동 소수점 수를 비교하려고 시도하지 말자. assertFloatEqualAbsassertFloatEqualRel는 기본 행위가 원하는 결과를 산출하지 않으면 특별히 절대적 차와 상대적 차를 테스트할 수 있다. 비슷하게 assertEqualItems와 assertSameItems 등등도 순열을 테스트할 때 유용하다.

코드에서 각 클래스의 인터페이스를 테스트하려면 ClassNameTests이라는 이름으로 TestCase 적어도 하나의 정의하면 된다. 여기에서 공개 인터페이스에 있는 무엇이든 검증되어야 한다.

클래스가 복잡하면, ClassNameTests_test_type이라는 이름으로 따로 더 테스트를 정의해도 된다. 이 테스트들은 setUp 메쏘드를 공유하기 위하여 ClassNameTests를 하부클래스화할 수도 있다, 등등.

사적 메쏘드에 대한 검증은 따로 ClassNameTests_private라고 불리는 TestCase에 있어야 한다. 사적 메쏘드는 구현이 바뀌면 변할 수 있다. 상황이 바뀌는데 테스트 케이스가 통과하는 것은 불필요하다. (어쨌거나 그 때문에 사적인 메쏘드이다). 물론 이런 테스트들이 디버깅에 유용하지만 말이다.

클래스 안에 있는 메쏘드를 모두 검증하자. 검증되지 않은 메쏘드는 무엇이든 버그가 있다고 간주해야 한다. 테스트 이름 짓기 관례는 test_method_name이다. 메쏘드 이름에서 앞이든 뒤이든 밑줄문자는 검증의 목적을 위하여 무시해도 된다; 그렇지만, 모든 테스트는 문자적으로 test라는 문자열로 시작되어야 unittest가 찾을 수 있다. 메쏘드가 특히 복잡하다면, 또는 점검해야할 사례가 완전히 다르게 여럿이라면, 다음과 같이 test_method_name_suffix 형태를 사용하자. 예를 들어, __init__을 테스트하려면 test_init_empty, test_init_single, test_init_wrong_type, 등등으로 사용하면 된다.

테스트 메쏘드에 좋은 문서화문자열을 작성하자. -v 명령어-줄 스위치로 테스트를 실행하여 자세하게 출력을 하면, 각 테스트에 대하여 한 줄에 ...OK 또는 ...FAILED와 함께 문서화문자열이 인쇄된다. 그리하여 문서화 문자열이 짧고 설명이 잘 되어 있는 것이 중요하며, 이런 상황에서 의미가 있다.

좋은 문서화문자열:
NumberList.var should raise ValueError on empty or 1-item list
NumberList.var should match values from R if list has >2 items
NumberList.__init__ should raise error on values that fail float()
FrequencyDistribution.var should match corresponding NumberList var

나쁜 문서화문자열:
var should calculate variance           # 클래스 이름이 없고, 기술적이지 못하다
Check initialization of a NumberList    # 무엇이 예측되는지 알려주지 않는다.
Tests of the NumberList initialization. # 上同

모듈-수준의 함수는, 이른바 modulenameTests라고 부르는 독자적인 TestCase로 검증되어야 한다. 이 함수들이 단순하다고 할지라도, 예상대로 작동하는지 점검하는 것이 중요하다.

계산기가 요구되는 한 개짜리 거대 사례보다 수작업으로 점검할 수 있는 여러 작은 사례들을 검증하는 것이 더욱 더 중요하다. 수치 계산에 스프레드시트를 믿지 말자 -- 대신 R(역주:통계패키지의 일종)을 사용하자!

모서리 케이스들을 모두 확실하게 검증하자: 입력이 None이나 '' 또는 0이나 음수라면 어떻게 될까? 이쪽이나 저쪽으로 분기시키는 조건을 야기하는 값들에는 무슨 일이 일어나는가? 엉터리 입력이 올바르게 예외를 일으키는가? 코드는 자신이 예상한 유형의 하부클래스나 수퍼 클래스를 잘 받아들이는가? 엄청나게 큰 입력을 받으면 어떤 일이 일어나는가?

순열을 테스트하려면, 원래 버젼과 뒤섞은 버전이 다른지 점검하자. 그러나 정렬될 원래 버전과 정렬된 뒤섞은 버전이 동일한가를 반드시 점검하자. 다른 지점에서 시작하면 반복해서 실행할 때마다 순열이 다른지 확인하자.

무작위 선택을 테스트하려면, (예를 들어, 1000개나 백만개의) 방대한 샘플에서 얼마나 많이 선택할지 이항 분포나 그의 정규 근사를 사용하여 추측하자. 테스트를 여러번 실행해서 평균의 3 표준 편차 안에 있는지 점검하자 (역주: 전체 측정값의 68.3%가 평균치의 1 표준편차 이내이고, 95.9%는 2 표준편차 이내이며, 99.7%는 3 표준편차 이내임을 말한다).

unittest 검증 모듈 구조를 보여주는 예:

#!/usr/bin/env python

"""통계를 위한 클래스, 테스트 NumberList 그리고 테스트 FrequencyDistribution.
"""
from unittest import TestCase, main #부동소수점수라면 대신에 나의 unittestfp를 사용하라
from statistics import NumberList, FrequencyDistribution

class NumberListTests(TestCase):      #TestCase를 물려 받는 것을 꼭 기억하라 
    """NumberList 클래스 테스트"""
    def setUp(self):
        """Define a few standard NumberLists."""
        self.Null = NumberList()            #빈 초기화 테스트
        self.Empty = NumberList([])         #빈 연속열로 초기화 테스트
        self.Single = NumberList([5])       #원소 하나
        self.Zero = NumberList([0])         #원소는 하나, 거짓 원소
        self.Three = NumberList([1,2,3])    #여러 원소
        self.ZeroMean = NumberList([1,-1])  #0이 아닌 원소, 평균 0
        self.ZeroVar = NumberList([1,1,1])  #0이 아닌 원소, 0이 아닌 평균, 편차 0
        #이 객체들은 모든 테스트가 공유하고, 
        #'test' 문자열로 시작하는 메쏘드가 호출될 때마다 새로 만들어진다.
        #(즉, 같은 객체는 테스트 사이에 견디지 못한다: 오히려, 따로 사본을 얻는다).

        def test_mean_empty(self):
            """빈 객체라면 NumberList.mean()은 ValueError를 일으켜야 한다"""
            for empty in (self.Null, self.Empty):
                self.assertRaises(ValueError, empty.mean)
        def test_mean_single(self):
            """리스트에 원소가 하나만 있으면  NumberList.mean()는 그 원소를 돌려준다"""
            for single in (self.Single, self.Zero):
                self.assertEqual(single.mean(), single[0])
        #평균 테스트 계속
		
        def test_var_failures(self):
            """원소가 <2 이라면 NumberList.var()는 ZeroDivisionError를 일으켜야 한다"""
            for small in (self.Null, self.Empty, self.Single, self.Zero):
                self.assertRaises(ZeroDivisionError, small.var)
        #변수 테스트 계속
        #다른 메쏘드 테스트

class FrequencyDistributionTests(TestCase):
    pass    # 코드 왕창 생략
# 다른 클래스의 테스트들

if __name__ == '__main__':    # 명령어-줄로부터 호출되면 테스트를 실행한다
    main()