SQLAlchemy, MongoEngine 또는 Django ORM 등의 쿼리를 보면 Chaining을 통해 쿼리를 쉽게 작성할 수 있다는걸 알 수 있다. 예를 들어 아래와 같은 형태이다.
User.objects.filter(user_id=1).order_by('-user_id')
위 쿼리는 user_id=1
의 결과값을 역순으로 정렬하여 return해준다. 쿼리에서 알 수 있듯이 filter().order_by()
형태로 함수들을 Chaining해줬다. 만약 Chaining을 지원하지 않았다면 filter()
를 한번해주고 그 결과값을 다시 order_by()
를 해주는 형태로 나눠서 작성해야 했을 것이다.
이는 상당히 많은 경우에서 유용한데, 파이썬의 경우 정말 간단한 방법을 통해 Method Chaining을 구현할 수 있다. 먼저 아래와 같은 코드가 있다고 가정한다.
class Engine:
def __init__(self):
self.query = 'SELECT * FROM temp'
def filter(self, **kwargs):
print(kwargs)
for args in kwargs:
self.query += f' {args}={kwargs[args]}'
def order_by(self, field):
self.query += f' ORDER BY {field[1:]}'
self.query += ' ASC' if field.startswith('-') else ' DESC'
def debug(self):
print(f'[*] query : {self.query}')
engine = Engine()
engine.filter(user_id=1).order_by('-user_id')
engine.debug()
그냥 간단하게 Django ORM의 형태를 갖춘 클래스이다. 위 코드를 실행시켜보면 AttributeError: 'NoneType' object has no attribute 'order_by'
라는 오류가 발생함을 알 수 있다. 해결 방법은 간단하다. 함수 마지막에 self
를 리턴해주면 된다. 따라서 위 코드는 아래와 같이 바뀔 수 있다.
class Engine:
def __init__(self):
self.query = 'SELECT * FROM temp'
def filter(self, **kwargs):
print(kwargs)
for args in kwargs:
self.query += f' {args}={kwargs[args]}'
return self
def order_by(self, field):
self.query += f' ORDER BY {field[1:]}'
self.query += ' ASC' if field.startswith('-') else ' DESC'
def debug(self):
print(f'[*] query : {self.query}')
engine = Engine()
engine.filter(user_id=1).order_by('-user_id')
engine.debug()
def filter()
함수의 가장 마지막에 return self
가 추가됐음을 알 수 있다. 실행시켜보면,
{'user_id': 1}
[*] query : SELECT * FROM temp user_id=1 ORDER BY user_id ASC
위와 같이 정상적으로 동작한다. 이유는 간단하다. 파이썬은 기본적으로 함수에서 반환값이 존재하지 않을 때 None
을 반환한다. 따라서 filter()
함수에서 None
이 반환되고, 결과적으로 None.order_by()
가 실행되는 형태이므로 당연히 실행이 되지 않는다. 하지만 return self
를 통해 객체 자체를 반환함으로써(위 예제에서는 Engine()
객체) Engine
객체 내부에 존재하는 order_by()
함수가 정상적으로 실행되는 것이다.
추가적으로 파이썬은 append()
처럼 객체를 직접 수정하는 함수는 Side-effect를 방지하기 위해 암묵적으로 None
을 리턴한다고 한다.
'Coding > Python' 카테고리의 다른 글
Python dataclass property/setter (0) | 2019.09.18 |
---|---|
Python Requests Retry (0) | 2019.09.05 |
ECS Blue/Green CodeDeploy 연동 배포 자동화 스크립트 (0) | 2019.06.21 |
Python zip으로 병렬처리 시 리스트의 크기가 다른 경우 (0) | 2019.06.14 |
MongoEngine과 Pymongo 같은 Connection 유지하는 방법 (0) | 2019.06.13 |