import sys
paper = [[0]*100]*100
input = sys.stdin.readline
for _ in range(int(input().rstrip())):
a, b = map(int, input().split())
for _ in range(a, a+10):
for __ in range(90-b, 100-b):
paper[_][__] = 1
area = 0
for _ in range(100):
for __ in range(100):
if paper[_][__] == 1:
area += 1
print(area)
예제 입력 1을 넣어봤는데, 계속 지나치게 큰 output이 나오더라고요.
그래서 코드를 다시 쭉 복기했더니 paper = [[0]*100]*100 부분을 shallow copy로 생성했더라고요.
즉, [[0]*100]*100이 모두 [[0]*100]을 참조하게 되어 해당 리스트의 값이 하나라도 밖이면 전부 같이 바뀌게 됩니다.
예를 들어,
paper = [[0]*5]*5
paper[0][0] = 1
print(paper)
를 쓰게 되면
[[1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0], [1, 0, 0, 0, 0]]로 모든 행의 첫 번째 열이 1로 변하게 됩니다.
따라서, 이를 방지하기 위해선 처음 리스트를 생성할 때 리스트 컴프리헨션을 사용해야 합니다.
paper = []
for _ in range(100):
paper.append([0]*100)
#########################################
paper = [[0]*100 for _ in range(100)]
이러면 각 내부 리스트가 독립적인 객체로 생성되기 때문에 얕은 복사 문제를 피할 수 있습니다.
여기에 맞게 코드를 수정하면 다음과 같습니다.
import sys
paper = [[0]*100 for _ in range(100)]
input = sys.stdin.readline
for _ in range(int(input().rstrip())):
a, b = map(int, input().split())
for _ in range(a, a+10):
for __ in range(90-b, 100-b):
paper[_][__] = 1
area = 0
for _ in range(100):
for __ in range(100):
if paper[_][__] == 1:
area += 1
print(area)
컴프리헨션(comprehension)이라고 인터넷에 검색하면 이해력이라는 뜻이 나오는데요. 리스트 컴프리헨션(list comprehension)을 단순히 리스트 이해력이라는 생각 하면 좀 이상합니다. 또 리스트 표현식이라는 표현도 쓰긴 하는데 expression이랑은 단어가 달라서 의미가 미묘하게 다른 것 같습니다. 그래서 어원사전에서 comprehension을 찾아봤는데요.
내용을 정리해 보면 통합하다는 뜻이 가장 가까워 보이고요. 여기에 맞게 해석하면 여러 줄로 정의되는 반복문을 한 줄로 통합한다라는 뜻인 것 같습니다. 리스트 반복통합식(?)이라고 해야 되나, 정확히 한글식 표현으론 뭘 써야 될지는 모르겠네요. 혹시 괜찮은 표현 있으면 댓글 부탁드립니다.
찾아보니 app registry가 준비되기 전에 특정 라이브러리가 import 되어 발생하는 오류였고, 처음 import 한 부분을 찾아가니 나의 경우는 처음에 asgi.py를 잘못 작성해서 생긴 문제였다.
오류가 생긴 asgi.py 코드는
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from accounts import routing
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings')
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(
URLRouter(
routing.websocket_urlpatterns
)
),
})
로 django_asgi_app = get_asgi_application() 부분이 routing이 auth 보다 뒤에 나오는 상황이었다.
오류 코드를 자세히 찾아보니 routing 부분에서 consumer를 불러오고 consumer에서rest_framework_simplejwt.tokens을 불러오는 부분에서 문제가 발생했고, app registry가 준비되기 전 해당 부분을 불러와서 발생한 오류임을 확인했다.
즉, get_asgi_application으로 app registry가 준비되기 전, routing에서 사용된 라이브러리 중 rest_framework_simplejwt.tokens.AccessToken이 먼저 불러와져 발생하는 오류였다.
간단하게 아래와 같이 코드 순서를 바꿔주어 오류를 수정하였다.
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings')
django_asgi_app = get_asgi_application()
from channels.auth import AuthMiddlewareStack
from accounts import routing
application = ProtocolTypeRouter({
"http": django_asgi_app,
"websocket": AuthMiddlewareStack(
URLRouter(
routing.websocket_urlpatterns
)
),
})
django를 배포하고 프런트와 합치면서 테스트하는 과정에서 신기한 현상을 발견했다. 임시로 접속할 수 있는 페이지를 만들기 위해 local memory cache를 사용해 권한을 확인하고 있었고 해당 페이지 테스트하고 있었다.
그런데 페이지를 새로고침 할 때마다 인증이 잘 될 때도 있고 안 될 때도 있는 것이다. 정확히는 서버 cache에 쿠키의 cache가 없어서 권한이 없다는 경우와 정상적으로 있어서 잘 동작하는 경우, 이 두 경우가 새로고침 할 때마다 랜덤으로 반복되었다. 보통 동작이 되면 되고 아니면 아닌데 어떻게 새로고침할 때마다 랜덤으로 오류가 발생하는일이 벌어지는 걸까?
왜 이런 일이 벌어진 걸까?
LocMemCache는 django를 동작하는 프로세스의 메모리에만 접근이 가능하다. 따라서 하나의 프로세스에선 문제가 없고 당연히 테스트 환경에서도 문제가 없다.
하지만 우리가 배포하는 환경은 다르다. 보통 django를 배포할 때 gunicorn으로 worker를 1이상으로 세팅하여 배포하게 된다, 이러면 worker마다 mem이 다르기 때문에 cache가 있는 worker에서만 정상 작동하게 되는 문제가 발생한다.