파이썬에서 가상환경을 만들면 그 안에서 패키지 등을 설치할 수 있고, 이는 해당 가상환경 안에서만 이뤄진 처리로, 가상환경을 비활성화하면 사용할 수 없다.

 

파이썬의 가상환경을 생성하는 방법은 다음과 같다.

터미널에서 다음과 같은 코드를 입력하면

python -m venv myenv

 

myenv 라는 이름의 가상환경이 생성된다.

 

그리고 이 가상환경을 활성화하고 싶으면 터미널에 다음과 같이 코드를 작성한다.

myenv\\scripts\\activate   #백슬래시 하나만 써도 됨

 

이제 터미널에 (myenv) 라는 가상환경 이름이 프롬프트에 같이 뜨게 된다.

 

만약 이 가상환경을 비활성화하고 싶다면 터미널에 다음과 같이 코드를 작성한다.

deactivate

 

정상적으로 작동했다면 터미널에 (myenv) 가 사라졌을 것이다.

 

여기까지 가상환경 생성의 기본적인 구조이다.

 

그렇다면 

python -m venv myenv

 

이 명령어가 하나하나 무슨 뜻인지 살펴보자.

 

 

1. python

  • 설명: python은 파이썬 실행 프로그램을 호출하는 명령어이다.
  • 기능: 파이썬 인터프리터를 실행하여 파이썬 명령어와 스크립트를 수행한다.

2. -m

  • 설명: -m 옵션은 파이썬 모듈을 스크립트처럼 실행하도록 한다.
  • 파이썬 모듈을 스크립트처럼 실행하도록 한다는 것은, 해당 모듈을 독립 실행형 스크립트처럼 실행하는 것을 의미한다. 이를 통해 모듈이 가지고 있는 기능이나 코드를 직접 실행할 수 있다.
  • 기능: 모듈 이름을 매개변수(parameter)로 받아 해당 모듈의 __main__ 부분을 실행한다. 여기서는 venv 모듈(입력값. argument)을 실행하는 것.

3. venv

  • 설명: venv는 파이썬 표준 라이브러리의 일부분으로, 가상환경을 생성하고 관리하는 모듈이다.
  • 기능: 가상환경을 생성하여 프로젝트별로 독립적인 파이썬 환경을 제공한다. 각 가상환경은 자체적인 파이썬 인터프리터와 패키지 디렉토리를 갖는다.

4. myenv

  1. 설명: myenv는 가상환경의 이름이다. myenv가 말고도 원하는 이름으로 변경할 수 있다.
  2. 기능: venv 모듈이 이 이름을 가진 디렉터리(폴더)를 생성하고, 그 안에 가상환경을 구성한다.
     
     
  3. 디렉터리 구조는 다음과 같다. vscode 왼쪽 탐색기에 myenv 라는 폴더가 생겼을 것이다.
    myenv/
    ├── bin/         # 실행 파일이 포함된 디렉터리 (Windows에서는 Scripts/ 라고 표현된다!!)
    ├── include/     # 헤더 파일 디렉터리
    ├── lib/         # 파이썬 라이브러리 디렉터리
    └── pyvenv.cfg   # 가상환경 설정 파일

     

전체 과정 요약

 

  1. 파이썬 인터프리터를 호출함.
  2. -m 옵션을 사용하여 venv 모듈을 실행함.
  3. venv 모듈이 myenv라는 이름의 가상환경 디렉터리를 생성.
  4. 가상환경이 myenv 디렉터리 안에 구성됨.

python -m venv는 venv 모듈을 실행하도록 파이썬에 지시하고, myenv는 가상환경의 이름으로 전달된.

 

이 명령어를 실행하면, myenv라는 이름의 가상환경이 생성되고, 이 가상환경을 활성화하여 프로젝트별로 독립적인 파이썬 환경을 사용할 수 있게 된다.

 

venv 모듈

 

venv는 "Virtual Environment"의 줄임말로, 파이썬 표준 라이브러리의 일부분이다.

이 모듈은 프로젝트별로 독립된 가상환경을 생성하고 관리하는 데 사용된다.

 

venv 모듈은 프로젝트마다 별도의 파이썬 환경을 설정함으로써, 서로 다른 프로젝트가 서로 다른 파이썬 패키지 의존성을 가질 수 있도록 한다. 이렇게 하면 한 프로젝트에서 패키지를 업데이트하거나 설치하더라도 다른 프로젝트에 영향을 미치지 않는다.

 

가상환경의 이점

 

 

  • 독립성: 프로젝트마다 독립된 파이썬 환경을 가질 수 있어, 서로 다른 프로젝트가 서로 다른 패키지 버전을 사용할 수 있다.
  • 관리 용이성: 각 가상환경은 프로젝트 디렉토리에만 관련된 패키지를 설치하므로, 관리가 간편해진다.
  • 안정성: 시스템 전체에 영향을 주지 않고, 프로젝트별로 필요한 패키지만을 설치하여 프로젝트 간 충돌을 방지한다.
     

 

venv 모듈 사용방법

 

1. 가상환경 생성:

 

myenv는 생성할 가상환경의 이름이다. 원하는 이름으로 변경할 수 있다.

python -m venv myenv

 

2. 가상환경 활성화: 체제에 따라 방법이 다르니 주의.

  • Linux/MacOS:
    source myenv/bin/activate
    

     

  • Windows:
    myenv\Scripts\activate

활성화된 후에는 터미널 프롬프트에 가상환경 이름이 표시된다 (myenv).

 

3. 패키지 설치: 가상환경 내에서 필요한 패키지를 설치한다.

pip install package_name #패키지 설치
pip list #설치된 패키지 목록 확인

.

.

 

4. 가상환경 비활성화: 작업이 끝난 후 가상환경을 비활성화한.

deactivate

 

 

 

더보기

cf.  파이썬 모듈과 스크립트

  • 모듈: 파이썬 파일(.py)로, 함수, 클래스, 변수를 정의하는 데 사용된다. 다른 파이썬 파일에서 import하여 사용할 수 있다.
  • 스크립트: 직접 실행 가능한 파이썬 파일로, 명령 줄에서 실행할 수 있는 명령어와 로직을 포함한다.

-m 옵션은 특정 모듈의 __main__ 부분을 실행하도록 한다. 즉, 해당 모듈이 독립적으로 실행될 때 실행되는 코드를 포함하는 부분을 수행한다. 모듈 파일 내에 if __name__ == "__main__": 블록이 있다면, 이 블록 내의 코드가 실행된다.

 

예시 : venv 모듈을 -m 옵션으로 실행

python -m venv myenv
  • 의미: venv 모듈의 __main__ 부분을 실행하여 myenv라는 이름의 가상환경을 생성한다.

 

다른 모듈 예시

 

예를 들어, example.py 파일이 다음과 같이 작성되어 있다고 가정

# example.py
def greet():
    print("Hello, world!")

if __name__ == "__main__":
    greet()

 

이 파일을 -m 옵션을 사용하여 실행하면

python -m example

 

example 모듈의 __main__ 부분이 실행되며, Hello, world!가 출력된다.

이를 통해 모듈을 독립적으로 테스트하거나 실행할 수 있다.

 

모듈(단일 파일)과 패키지(모듈의 모음집)의 사용법을 익히다가 import 함수가 적용되는 방법이 다르다는 것을 깨달았다.

 

💡모듈은 우리가 사용하는 파이썬 파일( .py) 하나하나를 모듈이라고 칭한다. (파이썬 파일을 저장할 때 .py라고 저장해야 파이썬 파일로 인식된다.)

 

그리고 중요한 건 패키지이다. 

패키지는 패키지라는 파이썬 파일을 따로 만드는 것이 아니다. 

패키지를 만든다는 것은,

폴더(패키지)를 만들고 그 안에 __init__.py 파일을 넣어주고(이게 있어야 파이썬이 패키지라고 인식한다.).

그 밑에 사용할 모듈들을 포함시켜주는 것이다.

 

💡즉, 파이썬에서 패키지는 여러 모듈을 모아놓은 폴더(디렉터리)이다.

그리고 이 폴더는 __init__.py 파일을 포함하여 패키지임을 명시한다. 이를 통해 파이썬은 해당 폴더를 패키지로 인식하고, 그 안에 있는 모듈들을 사용할 수 있게 된다.

 

  • 패키지: 여러 모듈을 포함하는 폴더. 패키지 폴더는 __init__.py 파일을 포함해야 함.
  • 모듈: 파이썬 코드가 들어 있는 개별 파일. 파이썬 .py 확장자를 가짐.
  • 파이썬 인식: 파이썬은 __init__.py 파일이 있는 폴더를 패키지로 인식하고, 그 안의 모듈들을 import할 수 있게 됨.

예시 디렉터리(폴더) 구조

my_package/
    __init__.py  # 패키지 초기화 파일
    module1.py   # 첫 번째 모듈
    module2.py   # 두 번째 모듈

 

my_package 폴더를 패키지로 사용하여 그 안의 모듈들을 임포트하고 사용할 수 있게 된다.

 

여기서 디렉터리 = 폴더 라고 생각하면 된다.

  • 디렉터리: 주로 운영체제와 관련된 용어로 사용된다. 파일 시스템에서 파일들을 정리하고 관리하는데 사용되는 구조.
  • 폴더: 그래픽 사용자 인터페이스(GUI)에서 흔히 사용되는 용어로, 파일을 정리하고 관리하기 위한 구조를 시각적으로 나타낸 것.

실제로 두 용어는 동일한 의미로 사용되며, 서로 대체 가능하다.

예를 들어, "디렉터리 구조를 설정하세요"와 "폴더 구조를 설정하세요"는 같은 의미이다.

 

 

이제 모듈과 패키지가 무엇인지는 정확하게 알았을 것이다!

 

 

그럼 이러한 모듈과 패키지를 다른 메인파일에서 불러와서 사용하기 위한 방법인 import 에 대해서 알아보자.

 

import는 파이썬에서 다른 모듈(코드 파일)이나 패키지(여러 모듈이 모인 폴더)를 현재 코드에서 사용할 수 있게 해주는 키워드이다. 쉽게 말해, 이미 작성된 다른 코드나 기능을 가져와서 현재 코드에서 활용할 수 있게 만드는 기능이다.

 

더보기

참고로 import는 함수가 아닌 키워드이다.

함수랑 키워드는 다른 개념이.

 

키워드 (Keyword)

  • 키워드는 파이썬 언어 자체에서 특별한 의미를 가지는 예약어이다.
  • 키워드는 변수 이름이나 함수 이름으로 사용할 수 없다.
  • import는 모듈이나 패키지를 현재 스크립트로 가져오기 위해 사용되는 키워드이다.

몇 가지 예시:

  • import
  • if
  • else
  • for
  • while
  • def (함수를 정의할 때 사용)

함수 (Function)

  • 함수는 특정 작업을 수행하도록 정의된 코드 블록이다.
  • 함수를 정의할 때는 def 키워드를 사용하며, 함수는 호출(call)되어야 실행된다.
  • 함수는 사용자 정의 함수와 내장 함수로 구분된다.

즉, 키워드는 파이썬 언어의 문법을 구성하는 예약어. 함수는 특정 작업을 수행하기 위해 정의된 코드 블록.

이때, 불러올 모듈이나 패키지와, 그들을 불러와서 사용할 메인파일은 같은 디렉터리 안에 존재해야 한다. 

무슨 말이냐면, 패키지 폴더(my_package)와 그 패키지를 사용할 스크립트 파일(main.py 등)이 동일한 부모 디렉터리에 위치해야 한다는 것이다.

예를 들어, my_project라는 이름의 폴더가 있다고 가정할 때, 그 안에 my_package 폴더와 main.py 파일이 있는 구조를 의미한다.

 

다른 디렉터리에 존재하는 모듈이나 패키지를 사용하기 위해서는 sys.path에 경로를 추가하거나, 환경변수를 사용하는 등의 복잡한 방식을 사용해야 하기 때문에, 일단 혼자 만들어서 연습하는 선에서는 같은 디렉터리 안에 포함시키는 것을 잊지 말자.

 

my_project/
    main.py
    my_package/
        __init__.py
        module1.py
        module2.py

 

이런 식으로 같은 폴더 안에 메인 파일과, 사용할 모듈이나 패키지가 같이 있어야 한다. 

 

 

이제 import 키워드를 어떻게 사용하는지 알아보자.

 

1. 단일 모듈 임포트

  • 특정 모듈 전체를 불러온다. 대신 이 경우 모듈 안에 있는 함수를 사용하기 위해서는 모듈명.함수명 써줘야 함
import math
print(math.sqrt(16))  # 출력: 4.0

 

2. 모듈에서 특정 함수나 변수 임포트: from 모듈 import 함수

  • 모듈에서 필요한 특정 함수나 변수까지 불러온다.
  • 이 경우 모듈에서 함수까지 불러왔기 때문에 그냥 함수명만으로 사용할 수 있음. ⭐
from math import sqrt
print(sqrt(16))  # 출력: 4.0

 

예시 

my_module.py라는 파일에 다음과 같은 함수가 있다고 가정:

# my_module.py
def greet(name):
    return f"안녕하세요, {name}님!"

 

이 함수를 다른 파일에서 사용하기 위해, import를 이용.

 

메인 파일 main.py

# main.py
from my_module import greet

print(greet('세은'))  # 출력: 안녕하세요, 세은님!

 

이렇게 하면 my_module 파일에 정의된 greet 함수를 main.py에서 사용할 수 있게 된다.

 

 

3. 모듈에 별칭 붙이기:

  • 모듈 이름이 길거나 사용하기 불편할 때 별칭을 붙일 수 있다.
import math as m
print(m.sqrt(16))  # 출력: 4.0

 

4. 패키지에서 모듈 임포트: from 패키지 import 모듈

  • 패키지 안에 있는 특정 모듈을 불러온다.
  • 이 경우, 2번과 같이 from ~ import ~ 와 같은 구조를 썼으나, module 안에 있는 함수를 사용하려면 모듈명.함수명 써야 한다.
from my_package import module1
print(module1.greet('세은'))  # 출력: 안녕하세요, 세은님!

 

근데 2번에서는 애초에 '모듈에서 모듈 안의 함수까지' 이미 명시적으로 불러온 거였으니까 어느 모듈의 어떤 함수이다(모듈명.함수명)라고 명시 안하고 사용할 수 있었던 거고 (이미 불러왔으니까 할 필요 없음)

4번은 '패키지에서 모듈을' 가져온다는 거였으니까, 거기서 한 단계 더 파고 들어서 함수를 가져오려고 하면

패키지에서 모듈을 불러오고도 (패키지 안의 어떤 모듈명. 해당 모듈에 있는 함수명)까지 해줘야 하는 것이다.

 

처음에 보면 무지막지하게 헷갈린다. 

 

나는 처음에 from 모듈명 import * 했을 때는 함수명만으로도 그냥 사용할 수 있었는데 

왜 from 패키지 import * 했을 때는 왜 불러온 모든 모듈 안에 있는 함수를 사용하려면 위의 경우와 다르게 함수명만으로 못 사용하고 모듈명.함수명 해줘야 하는지 이해가 가지 않았다. *는 모든 것을 불러오는 것이라고 생각했기 때문이다.

 

하지만 import * 의 경우 패키지와 모듈의 경우에서 다르게 작동한다고 한다.

 

 

모듈과 import *

 

모듈에서 import *를 하면, 모듈 내의 모든 함수와 변수를 현재 네임스페이스로 가져온다.

예를 들어, module1.py에 다음과 같은 함수가 있을 때

# module1.py
def greet(name):
    return f"안녕하세요, {name}님!"

 

이를 import *로 불러오면:

from module1 import *
print(greet('세은'))  # 출력: 안녕하세요, 세은님!

 

이 경우 greet 함수를 바로 사용할 수 있다.

 

 

패키지와 import *

 

패키지에서 import *를 사용하는 경우에는, 패키지의 모든 모듈을 한 번에 가져오지 않는다.

대신, 패키지의 __init__.py 파일에 어떤 모듈이나 함수를 가져올지 명시적으로 지정해놨어야 한다.

my_package/
    __init__.py
    module1.py
    module2.py

 

__init__.py 초기화 파일 설정(패키지 폴더 안에 이게 있어야 파이썬이 패키지라고 인식하고 import를 사용할 수 있다.)

# __init__.py
from .module1 import greet
from .module2 import farewell

__all__ = ['greet', 'farewell']
 
__init__.py에 모듈에서 어떤 함수를 불러올지 다 명시를 해놨다면,
이제 2번과 같이 from 패키지 import *를 사용했을 때에도 그냥 함수명만으로 모듈의 함수를 사용할 수 있다. (초기화 파일에 싸그리 명시 해놨으면.)
 
 
>>패키지 불러오기
from my_package import *
print(greet('세은'))         # 출력: 안녕하세요, 세은님!
print(farewell('세은'))     # 출력: 안녕히 가세요, 세은님!

 

  • 모듈: import *는 모듈 내의 모든 항목을 현재 네임스페이스로 가져온다.
  • 패키지: import *는 패키지의 __init__.py 파일에 명시된 항목만을 현재 네임스페이스로 가져온다. __all__ 리스트에 포함된 항목만 가져올 수 있다.
더보기

네임스페이스 (Namespace)

 

네임스페이스는 이름(변수명, 함수명 등)이 저장되는 컴퓨터?공간이다. 네임스페이스를 통해 이름이 충돌하는 것을 방지하고, 각 이름이 어떤 객체를 가리키는지를 관리한다.

네임스페이스 종류:

  1. 내장 네임스페이스: 파이썬이 시작될 때 생성되며, print, len과 같은 내장 함수들이 포함된다.
  2. 전역 네임스페이스: 모듈 단위로 생성되며, 모듈 내에서 정의된 변수와 함수들이 포함된다.
  3. 지역 네임스페이스: 함수 호출 시 생성되며, 함수 내에서 정의된 변수들이 포함된다.

import *의 역할

import *는 모듈 내의 모든 변수와 함수 등을 현재 네임스페이스로 가져오는 작업을 한다. 이를 통해 모듈 이름을 붙이지 않고도 모듈의 항목들을 바로 사용할 수 있게 된다.

예시:

# my_module.py
def greet(name):
    return f"안녕하세요, {name}님!"

def farewell(name):
    return f"안녕히 가세요, {name}님!"

 

이 모듈을 import *로 가져오면:

from my_module import *

print(greet('세은'))  # 출력: 안녕하세요, 세은님!
print(farewell('세은'))  #출력: 안녕히 가세요, 세은님!

 

길게 쓰는 거 귀찮다고 import * 사용하려고 했는데 이를 남용하는 것은 좋지 않다고 한다.

 

  • 네임스페이스 오염: import *를 남용하면, 이름 충돌이 발생할 수 있으며, 코드의 가독성이 떨어질 수 있기에 꼭 필요한 경우에만 사용하는 것이 좋다.
  • 명시적 임포트: from 모듈 import 함수 형태로 사용하는 것이 네임스페이스 충돌을 방지하고 코드의 명확성을 높이는 데 도움이 된다.

 

 위에서 분명 __init__.py이 빈 파일이어도 괜찮다고 했는데 __init__,py에 뭔가 명시해야 한다니. 이게 무슨 말일까?

 

__init__.py 파일

  • __init__.py 파일은 패키지 디렉터리에 반드시 있어야 하며, 이는 해당 디렉터리가 패키지임을 파이썬에게 알려준다.
  • 이 파일이 비어 있을 때도 패키지로 인식되며, 기본적으로 패키지를 초기화(초기 설정)하는 역할을 한다.

 

명시적 임포트와 __all__

  • __init__.py 파일에 불러올 모듈이나 함수를 명시적으로 적어두면, 패키지를 임포트할 때 어떤 모듈이나 함수가 기본적으로 로드될지 제어할 수 있다.
  • 이를 통해 패키지 사용자가 패키지 내부 구조를 신경 쓰지 않고 쉽게 사용할 수 있게 된다.

 

__all__ 의 역할

  • __all__은 패키지나 모듈에서 import *를 사용할 때, 어떤 항목이 import 될 지를 명시적으로 정의하는 목록이다.
  • 특이하게 생기긴 했는데, 코드를 보면 알 수 있듯이 __all__ 은 리스트 자료형이다.
  • 이는 문자열의 리스트로, 모듈이나 패키지에서 import *를 사용할 때 임포트될 모듈 안의 클래스, 함수, 변수 명 등 모듈 안에 포함된 항목을 __all__ = [' ~ ',' ~ '] 처럼 명시한다.
  • 즉, __all__ 리스트는 해당 모듈이나 패키지에서 import * 구문이 사용될 때 가져올 모듈 안의 항목들을 문자열로 포함하는 리스트이다.
  • __all__에 정의된 이름들만 현재 네임스페이스로 가져오게 된다.
예시 :
my_package/
    __init__.py
    module1.py
    module2.py
 
__init__.py 설정
# __init__.py
from .module1 import greet
from .module2 import farewell

__all__ = ['greet', 'farewell']

 

이렇게 하면 my_package를 import *로 불러올 때, __all__ 리스트에 명시된 greet와 farewell 함수만 현재 네임스페이스로 가져오게 된다.

더보기

여기서 . 은 왜 찍는지 궁금할텐데, 하 이것도 겁네 복잡하다.

 

간략하게 패키지 내에서 상대 경로를 사용할 때는 .을 사용하여 현재 디렉터리를 나타내는데,

모듈을 임포트할 때 명시적으로 쟤네가 지금 현재 패키지 내의 모듈임을 파이썬에게 알려주기 위해 사용되는 것이다. __init__.py 안에 저렇게 안 쓰면 오류난다.

현재 패키지에 있는 module1이라는 모듈에서 greet 함수 가져오라는 것.

 

 

  • 상대 경로 (Relative Import):
    • from .module1 import greet에서 .은 현재 디렉터리(패키지)를 나타낸다.
    • 이는 현재 패키지의 module1 모듈에서 greet 함수를 임포트하라는 의미.
  • 절대 경로 (Absolute Import):
    • from my_package.module1 import greet처럼 패키지의 전체 경로를 명시한다.
    • 절대 경로는 프로젝트의 루트 디렉터리부터 패키지와 모듈을 지정한다.

상대 경로를 사용하지 않으면, 파이썬은 현재 패키지 내부가 아닌, 전체 파이썬 모듈 경로에서 모듈을 찾으려고 시도할 수 있다. 이는 특히 패키지 내부에서 모듈을 임포트할 때 중요한데, 같은 이름의 모듈이 다른 위치에 있을 경우 혼란을 방지하기 위해 저렇게 현재 디렉터리에 있는 모듈들이라고 해줘야 한다.

 

  • __init__.py 파일은 패키지 초기화 파일로, 이 파일이 비어 있어도 패키지라고 인식시키는 역할은 한다.
  • 그러나, 패키지 사용성을 높이기 위해 __init__.py에 필요한 모듈이나 함수를 명시적으로 적어둘 수 있다.
  • __all__은 'import * 사용 시' 어떤 항목을 네임스페이스로 가져올지 정의한다. __init__.py 파일에 __all__ 리스트를 설정하여 어떤 항목을 임포트할지 지정할 수 있다.

즉, import *을 사용할 때에만 __init__.py에 명시적으로 적어둔 애들만을 import 해서 현재 네임스페이스로 가져오는 것이고, 이는 __all__ 리스트를 통해 정의된다.

 

그니까, 위와 같이 __init__.py 에 명시 해놓았더라도, import * 를 사용하지 않고 명시적으로 import 할 수 있다는 것!

# 명시적으로 함수 임포트
from my_package.module1 import greet
from my_package.module2 import farewell

print(greet('세은'))       # 출력: 안녕하세요, 세은님!
print(farewell('세은'))    # 출력: 안녕히 가세요, 세은님!

 

찐 마지막 요약

 

  • import *를 사용하면 __all__ 리스트에 명시된 항목들만 현재 네임스페이스로 가져오게 된다.
  • import *를 사용하지 않는 경우, 필요한 모듈이나 함수를 명시적으로 임포트하여 사용해야 한다.

 

 

 

 

 

 

 

string (문자열)과 tuple (튜플)이 불변자료형이라는 것은 알고 있다.

 

불변 자료형이란, 생성된 후에는 그 값을 변경할 수 없는, 내부 상태가 바뀌지 않는 자료형을 말한다.

따라서 이들은 인덱스와 = 연산자를 통해 값을 변경(수정)할 수 없다.

 

문자열과 튜플 외에 또 값을 변경할 수 없는 불변 자료형에는 무엇이 있는지 알아보자.

 

 

  • 정수 (int):
    • 예시: a = 10
    • 정수형은 값을 변경할 수 없다.
  • 부동 소수점 (float):
    • 예시: b = 3.14
    • 부동 소수점 (그냥 우리가 알고 있는 소수 생각하면 되는 듯)값도 불변이다.
  • 복소수 (complex):
    • 예시: c = 1 + 2j
    • 복소수형도 불변이다.
  • 불리언 (bool):
    • 예시: d = True
    • 불리언 값은 변경할 수 없다.
    • not 연산자를 사용하면 불리언 값을 반전시킬 수 있지만(True <> False), 이는 원래 값을 변경하는 것이 아니라, 새로운 값을 반환하는 것이다. 불리언 값 자체는 여전히 불변으로 남아있음.
    • 즉, not 연산자는 원래 값을 수정하는 것이 아니라 새로운 반대값을 반환하는 연산자인 것이다.  
  • frozenset:
    • 예시: e = frozenset([1, 2, 3])
    • frozenset는 파이썬의 내장 자료형으로, 기본적인 집합 자료형인 set과 비슷하지만 불변(immutable)이다. 한 번 생성된 후에는 수정할 수 없다.
    • 셋 자료형의 특성 상 순서가 없고 중복된 요소를 허락하지 않지만, 가변 자료형인 set { } 이랑 다르게 frozenset은 불변 자료형이라는 특징이 있다.

 

 

위에서 not 연산자가 자료형의 본질을 바꾸는 것이 아니라 그냥 새로운 값을 반환하는 기능을 했다.

이와 마찬가지로 자칫 자료형 자체를 바꾼 거 아닌가? 하고 오해할 수 있는 기능들이 있는데

(replace, upper, lower 등) 이들 역시 

해당 자료형은 그대로 있고, 그 자료형을 활용한 새로운 값은 '반환'하는 것일 뿐이니 헷갈리지 말자.

 

예시

original_string = "hello"
modified_string = original_string.replace("h", "j")

print(original_string)  # 출력: hello
print(modified_string)  # 출력: jello

 

 

 

* 참조

frozenset vs. tuple

  • 데이터의 목적:
    • tuple: 순서가 있는 불변 자료형. 위치가 중요할 때 사용.
    • frozenset: 순서가 없고, 중복을 허용하지 않는 불변 자료형. 중복을 제거하고, 집합 연산(합집합, 교집합 등)을 수행할 때 유용.
  • 사용 예시:
    • tuple: 데이터의 순서나 위치가 중요한 경우. 예를 들어, 좌표 (x, y) 같은 경우.
    • frozenset: 중복된 값을 제거하고, 집합 특성을 유지해야 하는 경우. 예를 들어, 여러 집합 간의 교집합이나 합집합을 계산할 때. (set 자료형이 합집합과 교집합, 차집합을 사용함)

 

  • set: 가변 자료형으로, 수정 가능하며 집합 연산을 지원.
  • frozenset: 불변 자료형으로, 수정할 수 없지만 여전히 집합 연산을 지원.

 

✏️ “가용 전력이 핵심” 데이터센터가 좋아하는 미국 도시 톱5

 

저번 주 IT 소식에서 AI와 같은 대규모 데이터를 다루는 산업이 커짐에 따라 데이터 센터에 부담이 커지고 있다고 했다.

이번 주는 그런 데이터 센터가 세워지기에 적합한 미국 도시에는 어떤 곳이 있는지 알아보자.

 

1. 라스베이거스와 리노

 

라스베이거스와 리노의 데이터센터 전력 용량은 수요를 충족하기 위해 현재 수준보다 953% 증가할 것으로 예상된다.

네바다주에서 가장 큰 전력 회사인 네바다 에너지는 지열, 수력, 태양 에너지와 같은 재생 가능한 자원으로 대부분의 전력을 생산한다.

 

2. 솔트레이크 시티

 

솔트레이크 시티는 유타주의 매력적인 세금 인센티브 덕분에 데이터센터가 급성장하고 있다.

유타 주 정부는 새로운 데이터 센터 개발에 대한 세금 인센티브를 제공하는데 관대해 메타, 구글과 같은 대기업이 저렴한 비용으로 유타주에서 데이터센터를 확장할 수 있도록 유치하고 있다.

 

3. 피닉스

 

애리조나주는 기존 전력 공급원보다 생산비용이 낮은 천연가스와 태양열과 같은 재생에너지원에 더 많이 의존하고 있다.

 

4. 애틀랜타

 

마이크로소프트는 3개 시설에 걸쳐 대규모 데이터센터 업그레이드를 위해 이 지역에 18억 달러라는 엄청난 금액을 투자하고 있다.

 

5. 댈러스-포트워스

 

댈러스-포트워스는 코로케이션 데이터센터 부지 비용에서 상위 20개 대도시 중 7위를 차지할 정도로 경제성이 뛰어나다.

 

 

세계 최대의 데이터센터 허브로 간주되는 노던 버지니아는 미국 데이터센터 산업을 선도하고 있으며, 향후 용량은 가장 가까운 경쟁자인 피닉스보다 두 배가 될 것으로 예상된다. 

데이터 센터 성장이 둔화되는 유일한 요인은 충분한 전력을 확보하지 못하는 것이다.

 

 

업원드는 일반적인 데이터 센터가 지역 경제에 2억 달러 이상을 투입해 1688개의 건설 일자리를 창출하고 157개의 영구적인 일자리를 유지한다고 추산했다. 또한 데이터 센터가 완공되면 연간 약 780만 달러의 임금에 기여하고 매년 평균 110만 달러의 세수가 증가한다.

https://www.itworld.co.kr/news/354098#csidxe18db87614ed3c1886d442ae6881a01 

 

“가용 전력이 핵심” 데이터센터가 좋아하는 미국 도시 톱5

클라우드 보안 솔루션 업체 업윈드(Upwind)의 새로운 보고서에 따르면, 데이터센터 용량 증가 예상치를 기준으로 라스베이거스와 리노는 미국에서

www.itworld.co.kr

 

 

 

✏️ "싸다고 덥석?" 중고 노트북 구매 시 꼭 피해야 할 실수 8가지

 

현재 사용하고 있는 노트북이 저렴하게 구매한 옛날 모델이라 사양에 한계를 느껴 새로운 노트북을 사야하나 고민하고 있는데 노트북.. 여간 비싼 게 아니더라.

중고 노트북이라도 사야하나 싶은 와중에 유용해 보이는 기사를 가져왔다.

 

중고 노트북 구매에는 위험이 따른다.

중고 노트북을 구매할 때 반드시 피해야 할 실수를 살펴보자.

 

1. 경고신호를 무시하는 것.

가격이 지나치게 낮은 노트북은 대개 거짓말이거나 문제를 숨기고 있을 가능성이 크다. 

적당한 가격에 적당한 노트북을 사는 게 좋은 가격에 형편없는 노트북을 사거나 사기를 당하는 것보다 훨씬 낫다.

상태가 좋아보여도 도난품일 수도 있고, 비공식적으로 수리돼 보증이 무효 처리됐을 가능성도 있다.

 

2. 질문하지 않는 것.

중고노트북을 살 때는 현재 소유자로붙 가능한 많은 정보를 얻는 것이 정말 중요하다.

해당 노트북을 얼마나 오래 사용했는지, 얼마나 혹사시켰는지, 배터리 상태 보고서를 제공할 수 있는지.

 

3. 직접 만져보고 테스트하지 않는 것.

매일 사용할 물건을 구매할 때는 직접 만져보고 테스트해보는 게 최선이다.

화면을 보고 배터리 테스트를 돌려보고 키를 확인하고 팬 소음을 들어보기.

 

4. 구형모델을 구매하는 것.

동일한 이름의 모델이 여러 세대에 걸쳐 출시된 경우를 조심해야 한다. 같은 세대에서도 내부 하드웨어가 완전히 다른 경우가 있다. 판매자에게 시스템 사양의 스크린샷을 요청하고 해당 하드웨어를 가진 모델의 리뷰를 찾아 새 제품일 때의 성능을 확인하라.

 

5. 반품정책을 확인하지 않는 것.

적절한 반품정책과 보호 조항을 제공하는 사이트나 마켓플레이스를 통해 구매하라.

 

6. 완벽한 배터리 수명을 기대하는 것.

이미 어느 정도 사용된 제품이라면 배터리 수명이 완벽하길 기대하는 건 무리다.

 

7. os 라이선스를 확인하지 않는 것.

기본적으로 포함된 os에 대한 유효한 라이선스(제품 키)가 있는지 반드시 확인해야 한다.

노트북 하드웨어를 업그레이드하거나 os를 재설치할 경우 라이선스키가 필요하다.

 

8. 초기화 혹은 재설정하지 않는 것.

안전을 위해서 새로 구매한 중고 노트북이 완전히 깨끗한 상태인지 의심된다면 직접 공장초기화를 실행하는 것이 좋다.

 


https://www.itworld.co.kr/news/354063#csidxc9af467acfb020c9c3194572d24597f 

 

"싸다고 덥석?" 중고 노트북 구매 시 꼭 피해야 할 실수 8가지

중고 노트북은 특히 고성능 모델을 구매하고자 할 때 많은 돈을 절약할 수 있는 훌륭한 방법이다. 최신 고급 노트북은 새 제품으로 구매할 경우 매

www.itworld.co.kr

 

 

 

✏️ 미 법무부, 구글에 크롬 매각 요구…구글 아성 무너질까

 

지금도 크롬 페이지를 통해서 글을 작성하고 있는 와중에 이게 무슨 일이지

 

미국 법무부가 기술 업계를 재편할 수 있는 역사적인 조치를 제안하며 구글에 대한 반독점 조치를 강화하고 있다.

법무부는 판사에게 2024년 8월 구글이 검색시장을 불법적으로 독점했다는 판결에 따라, 구글의 검색 시장 지배의 핵심 자산인 크롬 브라우저를 매각하도록 강제할 것을 요청했다.

법무부의 최신 권고안에는 AI 및 안드로이드 OS와 관련된 조치도 포함되어 있었다. 모두 구글의 핵심 광고 사업과 성장 중인 AI 사업에 영향을 미칠 가능성이 있다.

두 대통령 행정부에 걸쳐 진행되는 이 사건은 시장 경쟁을 억압한다고 비판받는 구글의 관행을 해결하는 데 목적이 있다.

구글이 통제하는 필수 데이터와 기술에 경쟁사가 더 쉽게 접근할 수 있도록 함으로써 경쟁을 촉진하려는 의도이다.

법무부는 '크롬, 구글 플레이, 안드로이드와 같은 제품이 구글 검색 및 관련 기능, 특히 AI를 포함한 새로운 검색 접근점과 기능을 경쟁사나 신규 진입자보다 유리하게 만드는 것을 방지하기 위해 행동적, 구조적 해결책을 고려하고 있다'라고 말했다.



https://www.itworld.co.kr/news/354049#csidx9ad7fc5517dd7f789c2944640535566 

 

미 법무부, 구글에 크롬 매각 요구…구글 아성 무너질까

미국 법무부(The US Department of Justice, DOJ)가 기술 업계를 재편할 수 있는 역사적인 조치를 제안하며 구글에 대한

www.itworld.co.kr

 

'주간 IT소식' 카테고리의 다른 글

주간 IT 소식 (24.12.07)  (3) 2024.12.07
주간 IT 소식 (24.11.30)  (1) 2024.11.30
주간 IT 소식 (24.11.17)  (6) 2024.11.17
주간 IT 소식 (24.11.10)  (2) 2024.11.10
주간 IT 소식 (24.11.03)  (7) 2024.11.03

- 함수의 return 값은 총 하나만 존재한다.

>>> def add_and_mul(a,b): 
...     return a+b, a*b

>>> result = add_and_mul(3,4)

#
함수는 a+b 와 a*b의 복수반환값을 (7,12)라는 튜플형태로 반환하고
따라서 result에 (7, 12)라는 튜플값이 저장된다.

 

이 함수를 실행하면 a+b a*b는 튜플값 하나인 (a+b, a*b)로 리턴된다.

즉, 함수의 복수 반환값은 튜플의 형태로 저장된다.

 

만약 return 구문을 여러 개 작성한다고 해도 첫 번째 return 구문을 만나면 해당 값을 리턴하고 함수를 빠져나간다.

>>> def add_and_mul(a,b): 
...     return a+b 
...     return a*b

 

두 번째 return 문인 return a * b는 실행되지 않는다.

 

- *args 를 사용하여 여러 개의 변수를 받을 수 있다. 여러 개의 변수를 입력하면 튜플값으로 반환해준다.

>>> def add_many(*args): 
...     result = 0 
...     for i in args: 
...         result = result + i   # *args에 입력받은 모든 값을 더한다.
...     return result

 

add_many(1, 2, 3)를 실행하면 *args는 (1, 2, 3)을 반환해서 for 문을 시작한다.

 

- **kwargs 키워드 매개변수

 **을 붙이면 매개변수 kwargs는 딕셔너리가 되고 Key=Value 형태의 입력값이 그 딕셔너리에 저장된다.

>>> def print_kwargs(**kwargs):
...     print(kwargs)


>>> print_kwargs(name='foo', age=3)
{'age': 3, 'name': 'foo'}

 

 

- 매개변수에 기본값(초기값) 미리 설정하기

 

함수의 매개변수에는 기본값을 =로 설정할 수 있는데, (매개변수 = 기본값)

이 경우 기본값을 설정하는 매개변수는 기본값을 설정하지 않는 매개변수보다 뒤에 위치해야 한다.

초기화하고 싶은 매개변수는 항상 뒤쪽에 놓아야 한다는 것.

함수는 매개변수를 받아서 기본값이 없는 매개변수에 먼저 할당하기 때문이다. (함수 설정 기본 룰.)

(name, age, man=True) # 가능

(name, man=True, age) # 불가능

 

(기본값 없는 매개변수, 기본값 설정한 매개변수, *args, **kwargs) 순으로 매개변수를 받을 수 있다.

 

 

- 함수 안에서 선언한 변수의 효력범위

 

함수 안에서 사용하는 매개변수는 함수 안에서만 사용하는 ‘함수만의 변수’이다.

함수 안에서 선언한 매개변수는 함수 안에서만 사용될 뿐 함수 밖에서는 사용되지 않는다.

입력값을 전달받는 매개변수 a는 함수 안에서만 사용하는 변수일 뿐, 함수 밖의 변수 a와는 전혀 상관없다는 뜻이다.

 

다음 예시를 보자.

# vartest_error.py
def vartest(a):
    a = a + 1

vartest(3)

print(a) # 오류

 

vatest(3)을 하면 함수 안의 a는 4가 되지만 함수 밖의 print(a)에서 사용한 변수 a는 어디에도 선언되지 않았기 때문에 오류가 발생한다.

 

함수 안의 변수의 값을 함수 밖에서 활용하고 싶다면

return 한 다음에 함수 밖 다른 변수에 할당해주자.

a = 1 
def vartest(a): 
    a = a +1 
    return a

a = vartest(a) 
print(a)

#2

 

a라는 변수에 1을 대입하고 함수의 매개변수로 넣어주고 함수 밖에서 그 실행값을 다시 a에 넣어주겠다고 한다.

위의 코드는 함수 안이랑 함수 밖 매개변수가 관계가 없다는 걸 보여주기 위해 저렇게 작성한 거고 

a = 1 
def vartest(i): 
    i = i +1 
    return i

a = vartest(a) 
print(a)

 

이 코드랑 같다.

 

함수 밖의 변수를 함수 안에서 사용하게 만들어주는 global() 함수도 있다.

a = 1 
def vartest(): 
    global a 
    a = a+1

vartest() 
print(a)

 

하지만 프로그래밍을 할 때 함수는 독립적으로 존재하는 것이 좋기 때문에 이 방법은 잘 사용하지 않는다.

 

 

- 함수를 만드는 예약어 lambda

 

lambda는 함수를 생성할 때 사용하는 예약어로, def와 동일한 역할을 한다. 함수를 한 줄로 간결하게 만들 때 사용한다. 

def를 사용해야 할 정도로 복잡하지 않거나 def를 사용할 수 없는 곳에 주로 쓰인다.

 

기본구조는 다음과 같다.

함수_이름 = lambda 매개변수1, 매개변수2, ... : 매개변수를_이용한_표현식

 

>>> add = lambda a, b: a+b
>>> result = add(3, 4)
>>> print(result)
7

 

lambda로 만든 함수는 return 명령어가 없어도 표현식의 결괏값을 리턴한다.

add는 2개의 인수를 받아 서로 더한 값을 리턴하는 lambda 함수이다.

Lv5. 가장 많이 팔린 품목은?

 

1. 각 고객이 구매한 모든 제품의 총 금액을 계산하고, 고객 이름, 총 구매 금액, 주문 수를 출력하는 SQL 쿼리를 작성해주세요.

 

조회해야 하는 컬럼

CustomerName, TotalAmount, OrderCount

 

테이블이 세 개가 나왔다.

테이블 세 개도 한꺼번에 join 할 수 있다!

SELECT *
FROM Customers c JOIN Orders2 o ON c.CustomerID = o.CustomerID JOIN Products2 p ON o.ProductID = p.ProductID

 

customers 테이블과 orders2 테이블을 공통컬럼으로 묶고(각 고객이 어떤 주문을 했는지에 대한 정보 결합), 거기다가 또 products2 테이블을 orders2 테이블과의 공통컬럼으로 묶어주면(각 주문에서 어떤 제품이 주문되었는지에 대한 정보 결합), customers 테이블을 기준으로, orders2와 products2 테이블의 데이터를 결합한 결과를 반환한다.

모든 조인은 customers 테이블의 각 행을 중심으로 이루어진다.

 

문제에서 주문을 아예 하지 않은 고객을 포함하라는 조건을 걸지 않아서 그냥 join(inner join 적용됨) 사용한 것 같은데,

주문을 아예 하지 않아 총주문금액이 null 이고 주문 수가 0인 데이비드도 결과에 포함하고 싶어서 나는 그냥 left join을 사용하겠다.

SELECT c.CustomerName, SUM(p.Price * o.Quantity) AS TotalAmount, COUNT(o.OrderID) AS OrderCount
FROM Customers c left JOIN Orders2 o ON c.CustomerID = o.CustomerID left JOIN Products2 p ON o.ProductID = p.ProductID
GROUP BY 1
ORDER BY 2 desc

 

 

고객별로, 구매한 모든 제품의 총 금액 (가격 * 수량)과, 주문 수(해당고객의 OrderID가 등장하는 행을 카운트)를 조회.

구매 제품의 총금액을 기준으로 내림차순 정렬.

 

2. 각 제품 카테고리별로 가장 많이 팔린 제품의 이름과 총 판매량을 조회하는 SQL 쿼리를 작성해주세요.

 

조회해야 할 컬럼

Category, Top_Product, TotalSold

 

SELECT p.Category, p.ProductName as Top_Product, sum(o.Quantity) as TotalSold
FROM Products2 p left JOIN Orders2 o ON p.ProductID = o.ProductID
GROUP BY 1,2
HAVING SUM(o.Quantity) = 
       (SELECT MAX(SumQuantity)
        FROM (SELECT p2.Category,SUM(o2.Quantity) AS SumQuantity
              FROM Products2 p2 left JOIN Orders2 o2 ON p2.ProductID = o2.ProductID
              GROUP BY p2.Category, p2.ProductID) AS Subquery
        WHERE Subquery.Category = p.Category
       );

 

조회해야 할 정보들이 있는 두 테이블을 합쳐서 

최종적으로 카테고리, 제품명 별로 총 판매량(수량의 합계)을 그룹화한다.

 

그룹화한 다음 조회의 조건을 준다.

최종적으로 조회할 총판매량은 최소쿼리에서 카테고리, 상품아이디 별로  총판매량을 구한 것에서 

서브쿼리의 카테고리가 메인쿼리의 카테고리와 같은 곳의 최댓값이어야 한다.

(상품아이디도 같이 그룹화할 컬럼으로 넣어줘야 같은 카테고리의 판매량 전체가 나오지 않고 그 카테고리의 상품 별로 총수량의 합계를 조회할 수 있다.)

 

 

 

 

HAVING 절이 절대 쉽지 않고 복잡한데다 쿼리문에서 중요한 역할을 하는데

강의에서는 왜 전혀 다루지 않으셨는지 이쯤되니 몹시 의문이다.

 

 

 

 

 

'사전캠프 퀘스트' 카테고리의 다른 글

달리기반 SQL Lv5 (2)  (0) 2024.11.22
달리기반 SQL Lv4 (2)  (0) 2024.11.21
달리기반 SQL Lv4 (1)  (0) 2024.11.21
달리기반 SQL Lv3  (0) 2024.11.02
달리기반 SQL Lv2  (0) 2024.11.01

Lv4 단골 고객님 찾기

 

두 테이블이 주어졌으니 join함수를 이용하는 문제일 것이다.

 

일단 조건에 서브쿼리문과 having 함수를 활용하라고 했으니 이들에 대해 알아보자.

 

💡subquery문이란 쿼리문 안에 서브로 들어간 구문으로, 서브쿼리 결과를 메인쿼리에 다시 활용하는 것이다.

여러 번의 연산을 수행해야 할 때, 조건문에 연산결과를 사용해야 할 때, 조건에 쿼리 결과를 사용하고 싶을 때 사용한다.

 

예시를 보자.

select order_id, restaurant_name, 
       if(over_time >= 0, over_time, 0) as 'overtime'
from (select order_id,
             restaurant_name,
             food_preperation_time -25 as 'over_time'
      from food_orders) a

 

서브쿼리문에는 이름을 붙여줘야 하기 때문에 a라는 이름을 서브쿼리를 감싸는 괄호 뒤에 붙였다.

 

서브쿼리에서 food_orders 테이블에서 order_id 컬럼, restaurant_name 컬럼,

그리고 food_preperation_time 컬럼에서 25를 빼준 값을 over_time이라는 별칭을 붙여 가져온다.

그리고 메인쿼리에서 서브쿼리에서 가져온 컬럼들을 조회하고

그 중 over_time을 활용해 조건을 붙이고 그것을 overtime이라는 별칭을 붙여 조회한다.

 

💡having 함수는 group by 절로 그룹화된 데이터에서 조건을 설정하는 데 사용된다.

 

✔️having 절은 그룹화가 된 에 그룹 별로 조건을 설정한다. 그룹화된 결과에 조건을 걸 때 사용한다.

 

예를 들어, 각 카테고리 별로 매출이 1000 이상인 제품을 찾으려면

SELECT category, SUM(sales) AS total_sales
FROM products
GROUP BY category
HAVING total_sales >= 1000

 

카테고리 별로 그룹화하여 sales의 합을 구해 total_sales 라고 이름 붙인 뒤

그룹화의 결과인 total_sales가 1000이상인 값만 반환한다.

 

다음과 같이 사용할 수도 있다.

SELECT month, SUM(revenue) AS total_revenue
FROM monthly_sales
GROUP BY month
HAVING SUM(revenue) = (
    SELECT MAX(SUM(revenue))
    FROM monthly_sales
    GROUP BY month
);

 

having 절에서 월별로 그룹화해서 총매출을 가져온 다음에 그 결과에 조건을 건다. 해당 총매출은 해당 그룹월에서 가장 높은 값이어야 한다고.

 

 

✔️where 절은 그룹화가 되기 에 각 행에 대한 조건을 설정한다. 그래서 주로 행 별 조건을 걸 때 사용한다.

SELECT *
FROM products
WHERE price > 100

 

price 컬럼의 데이터가 100보다 큰 행의 개수를 반환한다. 

 

 

1. 고객별로 주문 건수와 총 주문 금액을 조회하는 SQL 쿼리를 작성해주세요.

    a. 출력 결과에는 고객 이름, 주문 건수, 총 주문 금액이 포함되어야 합니다. 단, 주문을 한 적이 없는 고객도 결과에 포함          되어야 합니다.

조회해야 하는 컬럼명 : CustomerName / OrderCount(new!) / TotalSpent(new!)

 

조건 후반부를 보니 left join을 사용해야 할 것 같은 느낌이 온다. 공통컬럼인 CustomerID로 묶자.

주문자명이 있는 행을 세주고(해당 주문자의 주문건수) 주문자명 별로 총주문량을 구하기 위해 주문량을 sum 함수로 집계해준다. 

만약 총주문량이 null이거나 0이라면 주문을 한 적이 없다는 뜻이므로 총주문량을 0이라고 지정해준다. 

select c.CustomerName, count(c.CustomerName) as 'OrderCount', 
       case when sum(o.TotalAmount) = 0 or sum(o.TotalAmount) is null then 0
       else sum(o.TotalAmount) 
       end 'TotalSpent'
from orders o left join customers c on o.CustomerID = c.CustomerID
group by 1

 

여기서는 주문을 한 적 없는 고객이 없어서 0으로 처리되는 값은 없다. 

 

그리고 위에서는 case 문을 통해서 0이나 null 값을 처리했는데,

앞에서 사용한 coalesce 문을 통해서 컬럼의 데이터가 null 값인 경우 다른 원하는 값으로 대체 해줄 수 있다.

select c.CustomerName,count(o.OrderID) as OrderCount,
       coalesce(sum(o.TotalAmount), 0) as TotalSpent
from orders o left join customers c on o.CustomerID = c.CustomerID
group by 1

 

해당 고객이 주문한 게 아예 없어서 sum(TotalAmount)의 값이 null이라면 0으로 대체해주고 조회하라는 것이다.

 

2. 나라별로 총 주문 금액이 가장 높은 고객의 이름과 그 고객의 총 주문 금액을 조회하는 SQL 쿼리를 작성해주세요.

 

일단 고객별로 나라와 총 주문금액까지 조회하는 쿼리는 다음과 같다.

select c.Country, c.CustomerName as 'Top_Customer', sum(o.TotalAmount) as 'Top_Spent'
from orders o left join customers c on o.CustomerID = c.CustomerID
group by 1,2

 

 

이제 서브쿼리와 having 절을 사용해 각 나라에서 금액이 가장 높은 고객만 뽑아보자.

select c.Country, c.CustomerName as Top_Customer, sum(o.TotalAmount) as Top_Spent
from orders o left join customers c on o.CustomerID = c.CustomerID
group by 1,2
having sum(o.TotalAmount) = 
       (select max(SumSpent)
        from 
            (select sum(o2.TotalAmount) as SumSpent
             from customers c2 join orders o2 on c2.CustomerID = o2.CustomerID
             where c2.Country = c.Country
             group by c2.CustomerID) as Subquery
        )

 

처음에 작성한 코드로 모든 고객과 그 고객의 나라, 총 주문금액을 구했다.

그렇게 그룹화가 된 후에 having 절로 그룹별로 조건을 걸어준다.

 

각 그룹의 총 지출액 sum(o.TotalAmount) 이 서브쿼리의 최대 지출액 max(SumSpent)과 같아야 한다고 having 절을 설정한다.

 

최소(제일 안쪽) 서브쿼리는 고객별로 총 지출액을 계산하고, where 절로 각 국가의 최대 지출액을 찾는다.

  • where c2.Country = c.Country 조건을 통해, 현재 국가(c.Country)에 속한 고객만 선택한다.
  • 즉, 각 국가별로 데이터를 분리해준다.
  • group by c2.CustomerID를 통해 각 고객별로 데이터를 그룹화하여 각 고객의 총 지출 금액을 계산한다.
  • select sum(o2.TotalAmount) as SumSpent는 각 고객의 총 지출 금액을 계산하여 SumSpent로 이름 붙인다.

이제 이 SumSpent 값들 중에서 최대값을 찾기 위해서 또 다른 쿼리를 사용한다.

 

max(SumSpent)

  • 서브쿼리로 나온 결과 (SumSpent) 중에서 가장 큰 값을 선택한다.
  • 이 값이 바로 각 국가에서 가장 많이 지출한 금액이 된다.

 

 

having 절 사용하지 않고 구할 수 있는 방법은 없을까? 이 방법은 너무 복잡한 것 같다..

'사전캠프 퀘스트' 카테고리의 다른 글

달리기반 SQL Lv5 (1)  (0) 2024.11.22
달리기반 SQL Lv4 (2)  (0) 2024.11.21
달리기반 SQL Lv3  (0) 2024.11.02
달리기반 SQL Lv2  (0) 2024.11.01
달리기반 SQL Lv1  (0) 2024.11.01

셋 { } 을 컬렉션 자료형 중 집합 자료형이라고 부른다.

딕셔너리와 같이 중괄호 {} 를 사용하나 키값쌍이 없다.

 

가변자료형이지만 순서가 없고 중복을 허용하지 않는다는 특징이 있다. 

set은 중복을 허용하지 않는 특징 때문에 데이터의 중복을 제거하기 위한 필터로 종종 사용된다.

(컬렉션 자료형 중 불변인 건 튜플 ( )이다.)

 

set 자료형은 순서가 없기(unordered) 때문에 인덱싱을 통해 요솟값을 얻을 수 없다.

(딕셔너리 역시 순서가 없는 자료형이므로 인덱싱을 지원하지 않는다. 그냥 key 값을 통해 value 값을 얻을 뿐.)

만약 굳이굳이 set 자료형에 저장된 값을 인덱싱으로 접근하고 싶으면 리스트나 튜플로 변환한 후에 해야 한다.

 

셋의 특징 중 하나는 교집합, 합집합, 차집합을 구하는 것이다.

 

일단 다음과 같은 셋이 있다.

>>> s1 = set([1, 2, 3, 4, 5, 6])
>>> s2 = set([4, 5, 6, 7, 8, 9])

 

& 를 이용하면 교집합을 구할 수 있다.

>>> s1 & s2
{4, 5, 6}

 

또는 다음과 같이 intersection 함수를 사용해도 결과는 동일하다. (교집합)

>>> s1.intersection(s2)
{4, 5, 6}

 

| 를 이용하면 합집합을 구할 수 있다. 이때 4, 5, 6처럼 중복해서 포함된 값은 1개씩만 표현된다.

>>> s1 | s2
{1, 2, 3, 4, 5, 6, 7, 8, 9}

 

합집합을 구하고 싶으면 union 함수를 사용해도 된다.

>>> s1.union(s2)
{1, 2, 3, 4, 5, 6, 7, 8, 9}

 

**근데 &랑 |는 비트 연산자.. 아닌가? 이렇게도 사용할 수 있구나.

 

 

다음은 차집합을 구하는 방법이다.

- (빼기)를 사용하면 차집합을 구할 수 있다.

 

일단 차집합이 뭔지부터 알고 넘어가자. 

차집합은 집합에서 공통된 요소를 제외한, 특정 집합의 순수한 요소들로 구성된 집합을 말한다.

쉽게 말해, 하나의 집합에만 속하는 요소들을 나타내는 집합이다.

 

집합 A와 집합 B가 있다고 하자:

  • A = {1, 2, 3, 4}
  • B = {3, 4, 5, 6}

A에서 B를 뺀 차집합(A - B)을 구해보면:

  • A - B = {1, 2}
    • A를 기준으로 B와 공통되는 요소를 제외하고, + 거기에다가 A에만 속하는 요소들을 나타낸다.

B에서 A를 뺀 차집합(B - A)을 구해보면:

  • B - A = {5, 6}
    • 이는 B에 속하지만 A에 속하지 않는 요소들로 이루어져 있다.

이를 파이썬의 셋 자료형에 적용하면,

>>> s1 - s2
{1, 2, 3}
>>> s2 - s1
{8, 9, 7}

 

difference 함수를 사용해도 차집합을 구할 수 있다.

>>> s1.difference(s2)  #s1 - s2 라는 의미
{1, 2, 3}
>>> s2.difference(s1)
{8, 9, 7}

 

 

셋 관련 내장함수들

 

다른 컬렉션 자료형들의 내장함수와 같이

셋 이름 . 내장함수() 사용하면 된다.

 

- 1개의 값만 추가할 때는 add() 함수를 사용한다. (리스트는 append())

- 값을 여러 개 추가할 때는 update() 함수를 사용한다. (리스트는 extend())

- 특정 값을 제거하고 싶을 때는 리스트에서 사용한 remove() 함수를 똑같이 사용한다.

 

** 참고로 셋 자체가 이미 중복요소를 허용하지 않기 때문에

remove() 함수를 사용했을 때,

같은 값이 여러 개 있을 때 첫 번째로 나타나는 항목만 삭제해서, 다른 중복 요소는 여전히 남아있는 리스트 자료형과 달리,

셋 자료형은 이미 셋이 생성된 순간 remove(x)에서 x값은 하나밖에 존재하지 않기 때문에,

remove()를 사용해서 x값을 삭제하는 순간, 더 이상 그 셋 자료형에는 x라는 요소가 하나도 남아있지 않게 된다.

처음에 만약 셋을 a = {1,2,1,3} 이라고 작성했더라도, 이를 지정하는 순간 중복요소는 하나만 남게 되어

실제로는 a = {1, 2, 3}이라고 내부에서 돌아가게 되고, 이후 a.remove(1)을 하면 반환되는 값은 {2,3}이 되는 것이다!

딕셔너리에는 동일한 Key가 중복으로 존재할 수 없다.

동일한 Key가 2개 존재할 경우, 1개를 제외한 나머지 Key: Value 값이 모두 무시된다.

 

딕셔너리의 Key로 쓸 수 있느냐, 없느냐는 Key가 변하는(mutable) 값인지, 변하지 않는(immutable) 값인지에 달려 있다.

예를 들어, Key에 리스트는 쓸 수 없지만, 튜플은 쓸 수 있다.   

즉, 리스트는 그 값이 변할 수 있기 때문에 Key로 쓸 수 없다.

단, Value에는 변하는 값이든, 변하지 않는 값이든 아무 값이나 넣을 수 있다.

 

딕셔너리 쌍 추가하기

>>> a = {1: 'a'}
>>> a[2] = 'b'
>>> a
{1: 'a', 2: 'b'}

 

del 함수를 사용해서 del a[key]를 입력하면 지정한 Key에 해당하는 {Key: Value} 쌍이 삭제된다.

 

딕셔너리에서 key를 사용해 value 얻기

리스트나 튜플, 문자열은 요솟값을 얻고자 할 때 인덱싱이나 슬라이싱 기법 중 하나를 사용했다. 하지만 딕셔너리는 단 1가지 방법뿐이다. 그것은 바로 Key를 사용해서 Value를 구하는 방법이다. 

>>> grade = {'pey': 10, 'julliet': 99}
>>> grade['pey']
10
>>> grade['julliet']
99

 

 

딕셔너리 관련 내장함수들

 

다른 컬렉션 자료형과 마찬가지로 

딕셔너리 이름 . 내장함수 를 사용하여 활용할 수 있다.

 

- keys() 함수를 사용해 key리스트를 만들 수 있다.

a = {'name': 'pey', 'phone': '010-9999-1234', 'birth': '1118'}
print(a.keys())

#출력
dict_keys(['name', 'phone', 'birth'])

 

이는 다음과 같이 활용할 수 있다. 리스트를 사용하는 것과 별 차이는 없지만, 리스트 고유의 append, insert, pop, remove, sort 함수는 수행할 수 없다.

>>> for k in a.keys():
...    print(k)
...
name
phone
birth

 

 

- values() 함수를 사용해 value리스트를 만들 수 있다.

 

- items() 함수는 Key와 Value의 쌍을 튜플로 묶은 값을 dict_items 객체로 리턴한다.

>>> a.items()
dict_items([('name', 'pey'), ('phone', '010-9999-1234'), ('birth', '1118')])

 

- clear 함수는 딕셔너리 안의 모든 요소를 삭제한다.

>>> a.clear()
>>> a
{}

 

- get(x) 함수는 x라는 Key에 대응되는 Value를 리턴한다. 앞에서 살펴보았듯이 a.get('name') a['name']을 사용했을 때와 동일한 결괏값을 리턴한다.

딕셔너리 안에 찾으려는 Key가 없을 경우, 미리 정해 둔 디폴트 값을 대신 가져오게 하고 싶을 때는 get(x, '디폴트 값')을 사용하면 편리하다.

비어있는 리스트는 a = list() 로 생성할 수 있다.

 

리스트 안에는 아무 자료형이나 포함할 수 있다. (리스트 안에 리스트를 요소로 갖는 것도 가능)

 

a = [1, 2, 3, ['a', 'b', 'c']]

리스트 a의 마지막 요소인 리스트 ['a', 'b', 'c']에서 'a'를 인덱싱을 통해 꺼내올 수 있는 방법이 있다!

a = [1, 2, 3, ['a', 'b', 'c']]

print(a[-1][0]) 

#출력
a

 

print(a[-1][0]) 는 리스트의 마지막 요소인 리스트 ['a', 'b', 'c']에서 첫번째값을 꺼내와서 출력하라는 의미이다.

인덱싱을 한 번에 두 개 이상 할 수도 있다는 점!

 

리스트 역시 더하기가 가능하다.

>>> a = [1, 2, 3]
>>> b = [4, 5, 6]
>>> a + b
[1, 2, 3, 4, 5, 6]

 

del 함수를 사용하여 리스트의 요소를 삭제할 수 있다.

>>> a = [1, 2, 3]
>>> del a[1]
>>> a
[1, 3]

 

슬라이싱 기법을 사용하여 리스트의 요소 여러 개를 한꺼번에 삭제할 수도 있다.

>>> a = [1, 2, 3, 4, 5]
>>> del a[2:]
>>> a
[1, 2]

 

 

문자열과 마찬가지로 리스트 변수 이름 뒤에 . 를 붙여 여러 가지 리스트 관련 함수를 사용할 수 있다.

 

- append(x)는 리스트의 맨 마지막에 x를 추가하는 함수이다.

 

- sort 함수는 리스트의 요소를 순서대로 정렬해 준다. (문자 역시 알파벳 순서대로 정렬할 수 있다.)

 

- reverse 함수는 리스트를 역순으로 뒤집어 준다. 이때 리스트 요소들을 순서대로 정렬한 다음 다시 역순으로 정렬하는 것이 아니라 현재의 리스트를 그대로 거꾸로 뒤집는다.

 

- index(x) 함수는 리스트에 x 값이 있으면 x의 인덱스 값(위칫값)을 리턴한다. 이때 x값이 여러 위치에 중복해서 존재하면 그 위칫값을 전부 리턴하는 것이 아니라 첫번째로 등장하는 위칫값만 리턴한다. (사전캠프 퀘스트 파이썬 달리기3 참조) 

a = [1, 1, 2, 3, 3]

print(a.index(1))

#출력
0

 

- remove(x)는 리스트에서 첫 번째로 나오는 x를 삭제하는 함수이다. (x값이 중복되는 경우 첫 번째로 나오는 것만 삭제함)

그래서 x값이 나오는 거를 다 없애주고 싶으면 리스트 컴프리헨션 쓰라고 했었다. (이 역시 달리기3 참조)

 

- insert(a, b)는 리스트의 a번째 위치(컴퓨터 기준)에 b를 삽입하는 함수이다.

 

- pop()은 리스트의 맨 마지막 요소를 리턴하고 그 요소는 삭제한다.  실제 리스트값에 반영됨.

- 즉, pop(x)는 리스트의 x번째 요소를 리턴하고 그 요소는 삭제한다.

 

- count(x)는 리스트 안에 x가 몇 개 있는지 조사하여 그 개수를 리턴하는 함수이다.

 

- extend(x)에서 x에는 리스트만 올 수 있으며 원래의 a 리스트에 x 리스트를 더한다.

>>> a = [1, 2, 3]
>>> a.extend([4, 5])
>>> a
[1, 2, 3, 4, 5]
>>> b = [6, 7]
>>> a.extend(b)
>>> a
[1, 2, 3, 4, 5, 6, 7]

 

이 글은 점프 투 파이썬을 참조했다.

+ Recent posts