파이썬을 다루면서 알게 된 점 몇 가지
클래스 내외부 변수를 구분할 필요가 있다
파이썬에서는 클래스 변수를 선언할 때 self 객체를 이용하는 방법이 일반적이지만 이 방법은 정확히는 인스턴스에서 사용할 내부 변수를 사용하는 방법입니다. 해당 클래스 변수 접근을 위해서는 인스턴스 생성이 필수적으로 요구되기 때문에 모든 객체에서 동일값을 보장해야 하거나, C#이나 Java와 같은 다른 언어의 static 키워드와 비슷한 효과를 의도하고 싶은 경우 등에는 __init__() 함수 외부에 선언해야 합니다.
1
2
3
4
5
6
7
8
9
class Person:
''' 아래과 같이 선언할 수 있습니다.
'''
type = "person"
def __init__(self):
self.name = name
self.age = age
# ...
이 변수는 모든 인스턴스에서 동일한 값으로 제공됩니다. 이때 객체에 대한 접근은 심지어 객체 내부에서조차 클래스명.변수명과 같이 이루어짐에 주의해야 할 필요가 있습니다. 예를 들어 위의 People 클래스 내에서 type 변수값을 another_people_type와 같이 수정해야 하는 경우 People.type = another_people_type으로 접근할 수 있습니다.
파이썬도 getter, setter를 사용할 수 있다
C#, Java 등의 언어처럼 getter와 setter로 불리는 것은 아니지만, 클래스 내부 변수와 공개용 프로퍼티를 연결한다는 개념은 같습니다. 클래스 내부 변수를 은닉화하는 용도로도 유용하지만 그보다는 어떤 변수의 값이 변경될 때의 로직을 연결할 수 있다는 점에서 더 편리하므로 알아둘 필요가 있습니다.
1
2
3
4
5
6
7
8
9
10
class Example:
def __init__(self):
self._variable = None
@property
def variable(self):
return self._variable
@variable.setter(self, value):
self._variable = value
다른 언어에서 getter만 선언했을 때 읽기 전용으로 동작하는 것과 마찬가지로 @property만 선언하면 해당 프로퍼티로 변수를 수정하려고 할 때 오류가 발생하므로 주의해야 합니다.
모듈명에 클래스 개수를 반영할 수 있다
기본적으로 함수는 코드의 분류 단위, 클래스는 함수와 변수의 분류 단위, 모듈은 클래스의 분류 단위입니다. 이러한 관점을 개념적으로 더 잘 구현하는 방법중 하나는 한 개 모듈에 여러 개의 클래스가 있는 경우 모듈명을 단순히 복수형으로 명기하는 겁니다.
1
2
3
4
5
6
7
8
9
10
11
myproject/
│
├── app/
│ ├── users/
│ │ ├── models.py
│ │ ├── views.py
│ │ └── controllers.py
│ ├── products/
│ └── orders/
│
└── main.py
이때 models.py에는, 실제 프로젝트의 예시가 아니라서 구체적이지는 않지만 예를 들어 UserModel() 또는 ProductModel와 같이 이름 + 유형 형식의 클래스명을 사용할 수 있습니다. 이런 구조는 한 개 모듈에 한 개 클래스가 있을 때에 비해 클래스에 대한 분류가 명확해지고, 모듈을 사용할 때 from models import UserModel as UM와 같이 자연스러운 표현을 사용할 수 있다는 이점이 있습니다.
비유를 쓰는 것 보다는 직관적인 이름이 낫다
비유를 이용하는 경우 그 직관성은 대부분 반쪽짜리입니다. 그 반쪽 덕에 코드를 처음 접헀을 때 동작과정이 간결해지고 읽기가 더 재밌어지기는 하지만, 나머지 반쪽 덕에 코드가 작성된 의도가 모호해진다는 단점이 있어 되도록이면 피하는 것이 옳다고 생각합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def main():
''' 보석 세공 장인을 주제로 하면 이런 식
'''
self.mining()
self.cutting()
self.crafting()
self.selling()
def main()
''' 파인다이닝 셰프를 주제로 하면 이런 식
'''
self.washing()
self.cutting()
self.cooking()
self.plating()
코드의 전반적인 동작을 위와 같이 보석 판매를 주제삼아 채굴-절삭-세공-판매 과정에 빗대어 표현하거나, 요리사가 요리를 하는 과정을 흉내내어 세척-손질-요리-플레이팅의 구조로 표현하는 등 위의 예시 외에도 몇 가지 비유법을 사용해서 코드를 구조화해봤지만 비유법을 거친 함수명이 코드의 역할을 명확히 반영하지 않는다는 문제가 있었습니다.
이 때문에 의미를 잘 이해하기까지의 과정중 한 단계가 추가되어 타인 또는 미래의 나 자신이 코드를 읽을 때 의미를 직관적으로 이해하기에 불필요한 어려움이 발생했고, 간혹 주객전도가 이루어지는 양 클래스와 함수가 갖고 있는 테마를 의식하며 작성하는 경우도 있었습니다.
대신, 재미는 좀 없더라도 클래스의 역할을 정석적으로 작성하는 것이 깔끔했습니다.
1
2
3
4
5
6
def main():
''' 그냥 일반적인 경우
'''
download_data()
basic_process()
save_results()
사자성어나 유명 일화를 인용하는 등 비유에 있어서 보조관념이 가지는 직관성이 압도적일 경우에는 또 괜찮을 것 같기는 하지만, 이외의 대부분의 경우에는 위와 같이 코드의 동작 자체를 간결히 묘사하는 식으로 작성하는 것이 더 실용적이었습니다.