no image
[SQL] SQL 명령어(Keyword) 모음
SQL 명령어에 대해 소개해보려고 한다. 자주 사용하는 명령어 위주로 설명하고 이후에는 코테문제에서 사용했던 명령어들을 설명하려고 한다. 데이터다음은 user 테이블의 데이터이다.idnameageemail1John28John@example.com2Jane32Jane@example.com 3Bob24Bob@example.com 4Alice29Alice@example.com 5Tom31Tom@example.com 6Lucy22Lucy@example.com 7Mark35Mark@example.com 8Ana25Ana@example.com 9Yun28Yun@example.com  명령어명령어와 명령어에 대한 간단한 설명이다.키워드설명SELECT하나 이상의 컬럼의 데이터를 조회FROM데이터 조회의 대상이 되는 테..
2024.06.25
no image
[Django] 장고의 마이그레이션(Migration)
마이그레이션(Migration)은 무엇인가?Django의 마이그레이션은 모델의 변경 사항을 DB 스키마로 전파하는 프로세스다. 사용자(User)가 모델을 변경(필드 추가 및 삭제)를 하면, 이를 데이터베이스 스키마에 적용하는 것을 목적으로 설계했다. 마이그레이션을 생성하면 그 시점의 파이그레이션 구조를 나타낸다. git과 같이 그 시점의 commit 상태라고 보면된다.다시 말하면, 마이그레이션은 그 시점의 모델간의 모든 종속성(dependency)를 담고 있다고 보면 된다. setting.py 적용하기마이그레이션 관련 명령어를 적용하기 이전에 해야할 일이 있다. 그것은 settting.py에 app_name을 적용하는 것이다.config/setting.py에 들어가서 INSTALLED_APPS에  app..
2024.06.19
no image
[VSCode] csv파일 한글깨짐 해결방법
VSCode에서 CSV파일을 열 때, 한글이 깨졌을 때 해결하는 방법에 대해 알아보려고 한다.csv파일을 열게 되면 다음과 같이 화면이 깨져 나온다. 오류가 나는 이유는 인코딩에서 문제가 생긴다. 그래서 인코딩 방법을 수정해야한다. 수정하는 방법에 대해 소개하겠다. 한국어로 인코딩해서 출력우하단에 UTF-8을 클릭 인코딩하여 다시 열기 클릭 Korean 선택 한국어로 출력 한국어로 인코딩해서 저장EUC-KR 클릭  인코딩하여 저장(Save with Encoding) 클릭 UTF-8 클릭  이 과정을 거치면 다음에 파일을 열 때 자동적으로 한글로 잘 출력되는 것을 확인할 수 있다.
2024.06.14
no image
[Python] 파이썬에서 getter, setter 사용 방법(feat. Property)
파이썬에서 객체에 값을 저장하거나 가져올 때, getter와 setter를 사용하는데 이것들을 하나하나 선언하는 것 (get_name 또는 set_name)보다 파이썬에서 지원하는 것이 있다. 그것은 바로 Property(프로퍼티)이다. PropertyProperty는 클래스의 어떤 속성에 대한 접근을 제어하는 경우 사용한다. Property는 클래스 속성에 대해 접근을 하는 내장 함수이다. 이를 통해 클래스 속성의 값을 설정하거나 값을 가져올 때 추가적인 기능을 실행할 수 있도록 합니다. 이를 통해서 더욱 파이썬스러운 코드를 작성할 수 있습니다. getter, setter, deleter 사용방법기본적으로 Class 다음과 같이 선언되어있다고 해봅시다.class Person: def __init_..
2024.06.13
no image
[Python] 타입 힌트를 위한 typing 모듈에 대해 알아보자
Python에서 타입 힌트 등장 배경 Python은 동적 타입 언어로 알려져 있다. 이는 프로그래머가 변수의 타입을 미리 선언하지 않아도 되며, 프로그램 실행 시점에서 타입이 결정된다. 이러한 동적 타이핑은 개발의 유연성을 높이지만, 대규모 프로젝트나 복잡한 시스템에서는 종종 문제를 야기한다. 타입 오류가 런타임에서만 발견되기 때문에, 버그를 예방하고, 코드의 안정성을 향상시키기 어렵다. 이러한 문제점을 해결하기 위해 Python 3.5 버전에서 typing 모듈이 도입되었습니다. 타입 힌트(typing Module) Python의 typing 모듈은 개발자들이 변수, 함수 매개변수, 반환값 등의 데이터 타입을 명시적으로 선언할 수 있게 해주는 기능을 제공한다. 주로 코드의 가독성을 향상시키고, 정적 분..
2024.06.12
no image
[PEP] 함수 Docstring과 Type Hint 작성 방법
파이썬에서 유명한 라이브러리 코드를 보게 되면 무언가 많이 작성되어 있는 것을 볼 수가 있다. def generate_identified_filename(filename: Path, identifier: str) -> Path: """ Append a string-identifier at the end (before the extension, if any) to the provided filepath Args: filename: pathlib.Path The actual path object we would like to add an identifier suffix identifier: The suffix to add Returns: String with c..
2024.06.11
no image
[PEP] Pythonic한 Name Convention에 대해 알아보자
파이썬에서 사용하는 PEP(Program Enhance Proposal) 에서 Name Convention에 대해 알아보려고 한다. PEP는 파이썬 개발자들 간의 관습을 적어놓은 것이라고 생깍하면 된다. 파이썬 유저라면 파이썬 코드를 작성할 때 PEP-8을 이용해서 쓰는 것을 추천한다.목차피해야 할 이름Package and Module Names (패키지 모듈 이름)Class Name (클래스 이름)Type Variable Names (타입 변수 이름)Exception Names (예외 이름)Global Variable Names (전역 변수 이름)Function and Variable Names (함수, 변수 이름)Method Names and Instance Variables (메소드와 인스턴스 변수..
2024.06.10
no image
[Clean Code] 프로그래밍 명명 규칙인 Camel Case, Snake Case, Pascal Case, Kebab Case에 대해 알아보자
프로그래밍을 하다 보면 변수, 함수, 클래스 등을 정의할 때 대문자를 쓰거나 언더바('_')를 쓰는 경우를 볼 수 있는데 이에 대해 자세하게 알아보려고 한다. 요약명명 규칙설명예시Snake_case모든 글자를 소문자로 하며, 단어는 밑줄로 구분한다. new_object, my_website_name, user_profile camelCasePascalCase와 유사하지만 첫 글자는 소문자이며, 공백은 없다. newObject, myWebsiteName, userProfile PascalCase각 단어의 첫 글자를 대문자로 이며, 공백은 없다 NewObject, MyWebsiteName, UserProfile Kebab-case모든 글자를 소문자로 하며, 단어는 하이픈( '-' )으로 구분한다. new-o..
2024.06.09
YAML, YML이란 무엇인가?
오늘은 yaml과 yml 파일 확장자가 무엇인지 알아보려고 한다. 그 이유는 docker-compose 파일과 스키마 작성시 yaml 확장자 파일을 사용하는데 내가 이전에는 본적이 없던 파일 형식이지만, 자주 사용되는 것을 보게 되었고 이에 대해 공부했던 내용들을 정리하려고 한다. YAML, YML은 무엇인가?yaml( YAML Ain't Markup Language)는 데이터를 표현하는 방법 중에 하나이다. JSON이나 XML와 비슷한 형식으로 작성할 수 있어서 자주 사용하는 방식입니다. 마크업 언어보다 데이터 중심을 구성을 강조한다는 점이 있으며, 공백문자를 이용한 들여쓰기로 파일을 작성한다. 파일 형식은 Key-Value 형식으로 작성한다. YAML, YML의 차이YML은 YAML의 파일 확장자 중..
2024.06.08
no image
[Python] 'Fatal error in launcher: Unable to create process using' 오류 해결 방법
오류> pipFatal error in launcher: Unable to create process usingpip 오류의 발생원인은 다음과 같다.환경 변수 문제: PATH 환경 변수에 Python 설치 경로가 정확하게 설정되지 않았을 수 있다.버전 충돌: 사용하는 Python 버전과 설치된 패키지 버전이 서로 호환되지 않을 수 있다. 해결방법# pip 재설치python -m pip install --upgrade --force-reinstall pip 해결방법은 pip를 재설치하면 된다.
2024.06.04
728x90

 

SQL 명령어에 대해 소개해보려고 한다. 자주 사용하는 명령어 위주로 설명하고 이후에는 코테문제에서 사용했던 명령어들을 설명하려고 한다.

 

데이터

다음은 user 테이블의 데이터이다.

id name age email
1 John 28 John@example.com
2 Jane 32 Jane@example.com
3 Bob 24 Bob@example.com
4 Alice 29 Alice@example.com
5 Tom 31 Tom@example.com
6 Lucy 22 Lucy@example.com
7 Mark 35 Mark@example.com
8 Ana 25 Ana@example.com
9 Yun 28 Yun@example.com

 

명령어

명령어와 명령어에 대한 간단한 설명이다.

키워드 설명
SELECT 하나 이상의 컬럼의 데이터를 조회
FROM 데이터 조회의 대상이 되는 테이블을 지정
WHERE 특정 조건건을 만족하는 데이터만을 필터링하여 조회
INSERT INTO 테이블에 새로운 데이터 행을 추가
UPDATE 기존 테이블의 데이터를 수정
DELETE 테이블에서 데이터를 삭제
JOIN 두 개 이상의 테이블을 연결하여 데이터를 조회
GROUP BY 선택된 컬럼을 기준으로 결과 세트를 그룹화
ORDER BY 결과 세트를 특정 컬럼에 따라 정렬
CREATE TABLE 새 테이블을 생성
ALTER TABLE 기존의 테이블 구조를 변경
DROP TABLE 테이블을 데이터베이스에서 제거
LIKE 특정 패턴을 포함하는 데이터를 검색할 때 사용
ASC 특정 데이터를 오름차순으로 정렬
DESC 특정 데이터를 내림차순으로 정렬

 

예시

각각의 명령어(키워드)에 대한 코드, 설명과 예상결과다.

아래는 `users` 테이블을 이용해 각 SQL 키워드의 사용 예시, 그리고 각 예시에 대한 설명과 예상되는 결과를 구체적으로 설명한다.

 

1. SELECT

  • 형식 : SELECT 컬럼1, 컬럼2, ... FROM 테이블명;
SELECT name, age FROM users;

 

  • 설명: 하나 이상의 컬럼의 데이터를 조회합니다.
  • 예상 결과: 모든 사용자의 이름과 나이가 출력됩니다.

 

2. FROM

  • 형식 : SELECT 컬럼1, 컬럼2, ... FROM 테이블명;
SELECT age FROM users;
  • 설명: 데이터 조회의 대상이 되는 테이블을 지정합니다.
  • 예상 결과: users 테이블의 age 행이 출력됩니다.

 

3. WHERE

  • 형식 : SELECT 컬럼들 FROM 테이블명 WHERE 조건;
SELECT * FROM users WHERE age > 30;

 

  • 설명: 특정 조건을 만족하는 데이터만을 필터링하여 조회합니다.
  • 예상 결과: 나이가 30세를 초과하는 모든 사용자의 정보가 출력됩니다.

 

 

4. INSERT INTO

  • 형식: INSERT INTO 테이블명 (컬럼1, 컬럼2, ...) VALUES (값1, 값2, ...);
INSERT INTO users VALUES (11, 'Eva', 27, 'eva@example.com');
  • 설명: 테이블에 새로운 데이터 행을 추가합니다.
  • 예상 결과: users 테이블에 새로운 사용자 Eva (27세, eva@example.com)가 추가됩니다.

 

5. UPDATE

  • 형식: UPDATE 테이블명 SET 컬럼1 = 값1, 컬럼2 = 값2 WHERE 조건;
UPDATE users SET age = 33 WHERE id = 2;
  • 설명: 기존 테이블의 데이터를 수정합니다.
  • 예상 결과: 사용자 Jane의 나이가 33세로 업데이트됩니다.

 

6. DELETE

  • 형식: DELETE FROM 테이블명 WHERE 조건;
DELETE FROM users WHERE id = 10;

 

  • 설명: 테이블에서 데이터를 삭제합니다.
  • 예상 결과: 사용자 Sara가 users 테이블에서 삭제됩니다.

 

 

7. JOIN

  • 형식: SELECT 컬럼들 FROM 테이블1 JOIN 테이블2 ON 테이블1.컬럼 = 테이블2.컬럼;
SELECT users.name, orders.order_date FROM users JOIN orders ON users.id = orders.user_id;
  • 설명: 두 개 이상의 테이블을 연결하여 데이터를 조회합니다.
  • 예상 결과: 사용자의 이름과 그들의 주문 날짜가 출력됩니다.

 

8. GROUP BY

 

  • 형식: SELECT 컬럼1, 집계함수(컬럼2) FROM 테이블명 GROUP BY 컬럼1;

 

SELECT age, COUNT(*) FROM users GROUP BY age;
  • 설명: 선택된 컬럼을 기준으로 결과 세트를 그룹화합니다.
  • 예상 결과: 각 연령대별 사용자 수가 출력됩니다.

 

9. ORDER BY

  • 형식: SELECT 컬럼들 FROM 테이블명 ORDER BY 컬럼 ASC|DESC(내림차순| 오름차순);
SELECT name, age FROM users ORDER BY name ASC;
  • 설명: 결과 세트를 특정 컬럼에 따라 정렬합니다.
  • 예상 결과: 사용자 이름을 알파벳 순으로 정렬하여 출력합니다.

 

10. CREATE TABLE

  • 형식: CREATE TABLE 테이블명 (컬럼1 타입, 컬럼2 타입, ...);
CREATE TABLE new_users (id INT, name VARCHAR(100), age INT, email VARCHAR(255));
  • 설명: 새 테이블을 생성합니다.
  • 예상 결과: new_users라는 새로운 테이블이 생성됩니다.

 

11. ALTER TABLE

  • 형식: ALTER TABLE 테이블명 ADD|MODIFY 컬럼 타입;
ALTER TABLE users ADD column VARCHAR(255);
  • 설명: 기존의 테이블 구조를 변경합니다.
  • 예상 결과: users 테이블에 새로운 열 column이 추가됩니다.

 

12. DROP TABLE

  • 형식: DROP TABLE 테이블명;
DROP TABLE new_users;
  • 설명: 테이블을 데이터베이스에서 제거합니다.
  • 예상 결과: new_users 테이블이 데이터베이스에서 제거됩니다.

 

13. LIKE

  • 형식: SELECT 컬럼들 FROM 테이블명 WHERE 컬럼 LIKE '패턴';
SELECT name, email FROM users WHERE email LIKE '%example.com';
  • 설명: 특정 패턴을 포함하는 데이터를 검색할 때 사용합니다.
  • 예상 결과: "example.com"을 도메인으로 가지는 이메일 주소를 가진 모든 사용자의 이름과 이메일이 출력됩니다.

 

+ 추후 추가 예정

728x90
728x90

마이그레이션(Migration)은 무엇인가?

Django의 마이그레이션은 모델의 변경 사항을 DB 스키마로 전파하는 프로세스다. 사용자(User)가 모델을 변경(필드 추가 및 삭제)를 하면, 이를 데이터베이스 스키마에 적용하는 것을 목적으로 설계했다. 마이그레이션을 생성하면 그 시점의 파이그레이션 구조를 나타낸다. git과 같이 그 시점의 commit 상태라고 보면된다.

다시 말하면, 마이그레이션은 그 시점의 모델간의 모든 종속성(dependency)를 담고 있다고 보면 된다.

 

setting.py 적용하기

마이그레이션 관련 명령어를 적용하기 이전에 해야할 일이 있다. 그것은 settting.py에 app_name을 적용하는 것이다.

config/setting.py에 들어가서 INSTALLED_APPS에  app_name을 추가하면 된다.

# config/setting.py

from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent

# (중략)


INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app_name', # 설치되어 있는 app_name을 추가한다.
]

# (중략)

 

 

Migration 관련 명령어

명령어 설명
migrtaions 모델의 변경 사항을 기반으로 새로운 마이그레이션 파일 생성한다. python manage.py makemigrations [app_name]
migrate 생성된 마이그레이션을 적용한다.  python manage.py migrate [app_name]
sqlmigrate 특정 마이그레이션에 대한 SQL문을 표시한다. python manage.py sqlmigrate [migrtaion_file]
showmigrations 프로젝트의 모든 마이그레이션을 보여준다. python manage.py showmigrations [app_name]

 

python manage.py makemigrations [app_name]

마이그레이션을 생성하는 명령어다.

자신이 이전에 생성한 app_name의 model.py와 관련된 마이그레이션을 생성하는 명령어다. 

# app_name/models.py

from django.db import models

class Course(models.Model):
    major = models.CharField(max_length=50)
    year = models.IntegerField()
    classification = models.CharField(max_length=20)
    course_code = models.CharField(max_length=20)
    course_name = models.CharField(max_length=100)
    professor = models.CharField(max_length=50)
    timetable_period = models.CharField(max_length=100)
    timetable_time = models.CharField(max_length=100)
    credits = models.IntegerField()
    class_type = models.CharField(max_length=20)

    def __str__(self):
        return self.course_name

app_name 내부에 있는 models.py를 작성하고 명령어를 실행하면 다음가 같이 실행된다. 여기서 app_name은 timetable이다.

마이그레이션이 생성됨

그렇다면, app_name/migrations/0001_initial.py가 생성된 것을 확인할 수 있다.

혹시 여기서 No installed app with label 'app_name'이 출력된다면, setting.py 에서 installed app이 잘 안되어있으니 다시확인해보자.

 

python manage.py migrate [app_name]

생성된 마이그레이션 파일을 적용하는 명령어다. 

생성된 마이그레이션 파일(0001_initial.py)을 실제 DB에 적용한다. 여기서 이전 버전으로 되돌리는 것으로 가능하다.(0001이 적용되기 이전의 상태 등)

 

python manage.py sqlmigrate [migrtaion_file]

특정 마이그레이션에 대한 SQL문을 표시한다. 마이그레이션 파일이 SQL문으로 어떻게 적용되는지를 보여준다. 

마이그레이션 파일이 SQL문으로 적용된 것을 확인할 수 있다.

 

python manage.py showmigrations [app_name]

마이그레이션이 적용된 상태를 표시해준다.

현재 0001만 적용되어있는 것을 확인할 수 있다.

 

주의점

마이그레이션은 서로 종속성(dependency)를 가지고 있을 확률이 매우 높기 때문에 함부로 삭제하지 말고 잘 확인하는 것이 필요하다. (다들 git 사용할 때, 함부로 이전 commit 날리지 않듯이...) 이와 관련해서 꽤나 고생해서 기억이 납니다...

 

출처 : https://docs.djangoproject.com/en/5.0/topics/migrations/

728x90
728x90

VSCode에서 CSV파일을 열 때, 한글이 깨졌을 때 해결하는 방법에 대해 알아보려고 한다.

한글이 깨짐

csv파일을 열게 되면 다음과 같이 화면이 깨져 나온다. 오류가 나는 이유는 인코딩에서 문제가 생긴다. 그래서 인코딩 방법을 수정해야한다. 수정하는 방법에 대해 소개하겠다.

 

한국어로 인코딩해서 출력


우하단에 UTF-8을 클릭

UTF-8 클릭

 

인코딩하여 다시 열기 클릭

 

Korean 선택

 

한국어로 출력

한국어로 출력

 

한국어로 인코딩해서 저장


EUC-KR 클릭

 

 

인코딩하여 저장(Save with Encoding) 클릭

 

UTF-8 클릭

 

 

이 과정을 거치면 다음에 파일을 열 때 자동적으로 한글로 잘 출력되는 것을 확인할 수 있다.

728x90

'Program > VSCode' 카테고리의 다른 글

[VSCode] ssh 비밀번호 무한 반복 입력 오류 해결  (0) 2024.04.14
728x90

파이썬에서 객체에 값을 저장하거나 가져올 때, getter와 setter를 사용하는데 이것들을 하나하나 선언하는 것 (get_name 또는 set_name)보다 파이썬에서 지원하는 것이 있다. 그것은 바로 Property(프로퍼티)이다. 

Property


Property는 클래스의 어떤 속성에 대한 접근을 제어하는 경우 사용한다. Property는 클래스 속성에 대해 접근을 하는 내장 함수이다. 이를 통해 클래스 속성의 값을 설정하거나 값을 가져올 때 추가적인 기능을 실행할 수 있도록 합니다. 이를 통해서 더욱 파이썬스러운 코드를 작성할 수 있습니다.

 

getter, setter, deleter 사용방법


기본적으로 Class 다음과 같이 선언되어있다고 해봅시다.

class Person:
    def __init__(self, name):
        self._name = name  # 언더스코어는 보호된 변수를 의미

getter

getter의 경우는 @property 메서드만 사용해주시면 됩니다.

@property
def name(self):
    return self._name

다음과 같이 작성을 하면됩니다. 그리고 이 함수는 클래스 내부나 외부에 작성하셔도 됩니다. 다시말해서 위치는 상관이 없습니다. 

setter

setter의 경우는 @<property_name>.setter 라고 작성합니다. 또한, 내부에 값이 잘 들어갔는지에 대한 로직을 만들어서 오류를 미리 방지합니다.

@name.setter
def name(self, value):
    if not value:
        raise ValueError("Name cannot be empty")
    self._name = value

deleter

deleter의 경우는 @<property_name>.deleter 메서드를 사용합니다.

@name.deleter
def name(self):
    print("Deleting name")
    del self._name

 

코드와 예시는 다음과 같습니다.

class Person:
    def __init__(self, name):
        self._name = name  # 언더스코어는 보호된 변수를 의미

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        if not value:
            raise ValueError("Name cannot be empty")
        self._name = value

    @name.deleter
    def name(self):
        print("Deleting name")
        del self._name

# 사용 예
p = Person("John")
print(p.name)  # John
p.name = "Alice"  # 이름 변경
print(p.name)  # Alice
del p.name  # 이름 삭제

 

 

예시


이메일 작성 예시입니다.

import re

EMAIL_FORMAT = re.compile(r"[^@]+@[^@]+[^@]+")


def is_valid_email(potentially_valid_email: str):
    return re.match(EMAIL_FORMAT, potentially_valid_email) is not None


class User:
    def _init__(self, username):
        self.username = username
        self._email = None

	# getter
    @property
    def email(self):
        return self._email

	# setter
    @email.setter
    def email(self, new_email):
        # 추가 로직 작성
        if not is_valid_email(new_email):
            raise ValueError(f"유효한 이메일이 아니므로 {new_email} 값을 사용할 수 없음")
        self._email = new_email
    
    # deleter
    del self._email
    
    
# 출력
>>> u1 = User("jsmith ")
>>> u1.email = "jsmith@"
Traceback
...
유효한 이메일이 아니므로 jmith@ 값을 사용할 수 없음
>>> u1.email = "jsmith@g.co"
>>> u1.email
'jsmith@g.co'

getter와 setter을 이용해서 유저의 이메일 정도를 가져오거나 수정할 수 있도록 합니다.

728x90
728x90

Python에서 타입 힌트 등장 배경


Python은 동적 타입 언어로 알려져 있다. 이는 프로그래머가 변수의 타입을 미리 선언하지 않아도 되며, 프로그램 실행 시점에서 타입이 결정된다. 이러한 동적 타이핑은 개발의 유연성을 높이지만, 대규모 프로젝트나 복잡한 시스템에서는 종종 문제를 야기한다. 타입 오류가 런타임에서만 발견되기 때문에, 버그를 예방하고, 코드의 안정성을 향상시키기 어렵다. 이러한 문제점을 해결하기 위해 Python 3.5 버전에서 typing 모듈이 도입되었습니다.

 

타입 힌트(typing Module)


Python의 typing 모듈은 개발자들이 변수, 함수 매개변수, 반환값 등의 데이터 타입을 명시적으로 선언할 수 있게 해주는 기능을 제공한다. 주로 코드의 가독성을 향상시키고, 정적 분석 도구를 사용한 오류 검출, 그리고 개발 환경에서의 향상된 자동 완성 및 코드 분석을 위해 사용한다. 

주요 기능은 다음과 같다.

  • 기본 데이터 타입: int, float, str, bool 등 Python의 기본 데이터 타입.
  • 컨테이너 타입: List[T], Dict[K, V], Set[T], Tuple[T, ...] 등과 같이 요소의 타입을 명시할 수 있는 컨테이너.
  • 복합 타입: Union[T1, T2]은 T1 또는 T2 타입의 값이 될 수 있음을 나타내고, Optional[T]은 None이 될 수 있는 T 타입을 의미.
  • 특수 타입: Any는 모든 타입을 허용하며, Callable[[Arg1, Arg2], ReturnType]는 호출 가능한 객체의 매개변수와 반환 타입을 명시.
  • 사용자 정의 타입: NewType을 사용하여 기존 타입에 대한 새로운 타입 이름을 생성할 수 있으며, 이는 추가적인 타입 안전을 제공.

코드를 작성하고 함수를 확인하면 타입에 대한 힌트가 보이게 된다.

함수를 작성하면, type hint가 보이게 됨

 

 

 

예시


설명하는 것들은 다음과 같다.

  • 기본 데이터 타입 : int, flaot, str, bool
  • 컨테이너 타입 : List, Dict, Tuple, Mapping, Iterable, NamedTuple
  • 복합 타입 : Optional, Union
  • 특수 타입 : Callable, NewType

 

기본 데이터 타입(int, float, str, bool)

def practice(a: int, b: float, c: str, d: bool) -> None:
    print(a, b, c, d)


practice(3, 3.14, "pi", True)
>>> 3 3.14 pi True

 

 

컨테이너 타입(List, Dict, Tuple, Mapping, Iterable, NamedTuple)

typing.List

List[T]는 특정 타입 T의 요소들로 구성된 리스트를 나타낸다.

from typing import List

def process_numbers(numbers: List[int]) -> int:
    """리스트에 포함된 숫자들의 합을 반환합니다."""
    return sum(numbers)

# 함수 사용 예시
result = process_numbers([1, 2, 3, 4, 5])
print(result)  
>>> 15

 

typing.Tuple

Tuple[T, ...]는 타입 T의 요소들로 구성되지만, 요소의 수가 고정되지 않은 튜플을 나타낸다.

from typing import Tuple

def multiply_elements(values: Tuple[int, ...]) -> int:
    """튜플 내 모든 정수의 곱을 반환합니다."""
    result = 1
    for value in values:
        result *= value
    return result

# 함수 사용 예시
result = multiply_elements((1, 2, 3, 4))
print(result) 
>>> 24

 

typing.Dict

Dict[K, V]는 키의 타입이 K이고 값의 타입이 V인 딕셔너리를 나타낸다. 예를 들어, 문자열 키와 정수 값으로 구성된 딕셔너리에 대한 타입 힌트는 다음과 같다.

from typing import Dict

def total_scores(scores: Dict[str, int]) -> int:
    """딕셔너리에 저장된 모든 점수의 합을 반환합니다."""
    return sum(scores.values())

# 함수 사용 예시
scores = {"Alice": 90, "Bob": 85, "Charlie": 88}
total = total_scores(scores)
print(total)  
>>> 263

 

typing.Mapping

Mapping은 키와 값의 쌍으로 데이터를 저장하는 구조를 나타내며, dict와 유사하지만 보다 일반적인 인터페이스를 제공한다. 

from typing import Mapping

def show_mapping(data: Mapping[str, int]) -> None:
    for key, value in data.items():
        print(f"{key}: {value}")

show_mapping({'one': 1, 'two': 2}) 
>>> one: 1, two: 2

 

typing.Iterable

Iterable은 요소를 하나씩 반환할 수 있는 컨테이너 타입을 나타낸다. 이는 리스트, 세트, 튜플 등과 같은 여러 컬렉션 타입을 포함할 수 있다. 간단히 말하면 len 함수를 사용할 수 있는 Type은 가능하다고 볼 수 있다. 

from typing import Iterable

def process_values(values: Iterable[int]) -> int:
    return sum(values)

# 리스트를 함수에 전달
print(process_values([1, 2, 3, 4]))  
>>> 10

 

typing.NamedTuple

NamedTuple은 튜플의 하위 클래스를 만들어 필드에 이름을 부여할 수 있게 한다. 이는 튜플의 각 요소에 접근할 때 인덱스 대신 이름을 사용할 수 있게 해서 코드의 가독성을 높인다.

from typing import NamedTuple

class Employee(NamedTuple):
    name: str
    id: int

# 사용 예시
emp = Employee("Alice", 100)
print(emp.name)  
>>> 'Alice'

 

복합 타입(Optional, Union)

typing.Optional ( = ' | ')

Optional[T] 타입은 변수가 None이거나 T 타입의 값을 가질 수 있음을 나타내며 기술적으로 Optional[T]는 Union[T, None], T | None과 동일하다.

from typing import Optional

def greet(name: Optional[str] = None) -> str:
    if name is None:
        return "Hello, Guest!"
    else:
        return f"Hello, {name}!"
        
# 함수 사용 예시
print(greet("Alice"))
>>> 'Hello, Alice!'
def greet(name: Union[str, None] = None) -> str:
    if name is None:
        return "Hello, Guest!"
    else:
        return f"Hello, {name}!"
from typing import Optional

def greet(name: str | None = None) -> str:
    if name is None:
        return "Hello, Guest!"
    else:
        return f"Hello, {name}!"

 

typing.Union

Union 타입은 변수가 가질 수 있는 여러 타입 중 하나임을 나타내는 타입 힌트다. Union은 변수가 여러 다른 타입 중 하나를 가질 수 있음을 명시할 때 사용된다.

from typing import Union

def length_or_value(data: Union[str, int]) -> int:
    if isinstance(data, str):
        return len(data)
    elif isinstance(data, int):
        return data

# 함수 사용 예시
print(length_or_value("hello"))
>>> 5
print(length_or_value(123))  
>>> 123

 

특수 타입(Callable, NewType)

typing.Callable

Callable 타입 힌트는 함수의 인터페이스를 명확히 하여, 다른 개발자가 해당 함수의 사용 방법을 쉽게 이해할 수 있도록 한다.

구조는 다음과 같다.

Callable[[ArgType1, ArgType2, ..., ArgTypeN], ReturnType]

여기서 ArgType은 함수의 입력을 의미하고 ReturnType은 함수 출력의 타입을 의미한다.

예시를 작성하면 다음과 같다.

from typing import Callable, Union

def operate_advanced(func: Callable[[int, float], Union[int, float]], x: int, y: float) -> Union[int, float]:
    """함수 func에 정수 x와 부동소수점 y를 전닯하고 결과를 반환"""
    return func(x, y)

def multiply(a: int, b: float) -> float:
    """정수와 부동소수점 수의 곱을 반환"""
    return a * b

# `multiply` 함수를 `operate_advanced`에 인자로 전달
result = operate_advanced(multiply, 4, 0.5)
print(result)  
>>> 2.0

operate_advanced(func: Callable[[int, flaot], Union[int, float]], x: int, y: float)

이 부분에서 Callable 이후 첫번째 요소는 함수의 입력들을 의미하며 각각 int, float이 입력으로 들어가고 출력은 int와 float 요소 중 하나를 반환한다는 것을 의미한다. Callable이 끝난 이후 function parameter에 대한 Type hint를 알려준다. 

 

typing.NewType

NewType을 사용하면 기존 타입에 기반한 새로운 타입을 생성할 수 있다. 이는 타입 체크 시에 기존 타입과 구별된다.

from typing import NewType

UserId = NewType('UserId', int)

def get_user_name(user_id: UserId) -> str:
    return "John Doe"  # 예시 목적

# 사용 예시
user = UserId(123)
print(get_user_name(user))  
>>> "John Doe"
728x90
728x90

파이썬에서 유명한 라이브러리 코드를 보게 되면 무언가 많이 작성되어 있는 것을 볼 수가 있다. 

def generate_identified_filename(filename: Path, identifier: str) -> Path:
    """
    Append a string-identifier at the end (before the extension, if any) to the provided filepath

    Args:
        filename: pathlib.Path The actual path object we would like to add an identifier suffix
        identifier: The suffix to add

    Returns: String with concatenated identifier at the end of the filename
    """
    return filename.parent.joinpath(filename.stem + identifier).with_suffix(filename.suffix)

* transformers 의 코드 중 하나

 

이런 식으로 함수 아래에  """ 따옴표를 이용하여 무언가가 많이 적혀 있는데 이것을 Docstring이라고 하는데 이것에 대해 알아보려고 한다. 

Docstring이란?


쉽게 말해서 소스 코드에 포함된 문서라고 말할 수 있다. docstring은 문자열이며, fucntion logic을 문서화하기 위해 작성한다. Docstring은 주석과는 다르게 Python의 첫 번째 실행 가능한 문장으로 작성되며, Python 인터프리터에 의해 처리되어 해당 객체의 __doc__ 특수 속성에 저장됩니다. 이를 통해 프로그램 실행 중에도 해당 정보에 접근할 수 있다.

 

Docstring 작성 방법


  1. 함수 정의와 타입 힌트 적용: 함수 정의에서 매개변수와 반환 값에 타입 힌트를 적용
  2. 문자열 시작: 함수 정의 바로 아래에 삼중 따옴표 (""")를 사용해 docstring을 시작
  3. 요약 설명: 함수가 수행하는 작업에 대한 간결하고 명확한 한 줄 요약을 포함
  4. 상세 설명 (선택 사항): 요약 설명 다음에 빈 줄을 두고 필요한 경우 함수의 동작을 보다 상세히 설명
  5. 매개변수 설명: Parameters 섹션에서 각 매개변수의 이름, 타입, 그리고 설명을 제공
  6. 반환 값 설명: Returns 섹션에서 함수가 반환하는 값의 타입과 그 의미를 설명
  7. 예외 설명: Raises 섹션에서 함수 실행 중 발생할 수 있는 예외와 그 상황을 나열하고 설명
  8. 예시 설명: Examples 섹션에서 함수의 실행 예시와 결과 값에 대해 나열하고 설명

 

타입 힌트 작성


타입 힌트는 typing 모듈과 pydantic 모듈을 사용합니다. typing은 3.5 버전부터 일반적으로 사용하고 3.10 버전부터는 pydantic 모듈을 사용한다. 특히 pydantic은 FastAPI와 같은 API에서 많이 사용하고 그 외에는 거의 typing 모듈을 사용한다고 보면 된다. 

 

예시


typing 모듈을 사용하지 않았을 때

def add(a: int, b: int) -> int:
    """
    두 정수의 합을 계산하여 반환합니다.

    Parameters:
    a (int): 첫 번째 정수. 정수가 아닐 경우 TypeError 발생.
    b (int): 두 번째 정수. 정수가 아닐 경우 TypeError 발생.

    Returns:
    int: 두 수의 합

    Raises:
    TypeError: `a` 또는 `b`가 정수가 아닐 때 발생

    Examples:
    >>> add(10, 20)
    30

    >>> add("ten", 20)
    TypeError: Both arguments must be integers
    """
    if not isinstance(a, int) or not isinstance(b, int):
        raise TypeError("Both arguments must be integers")
    return a + b

 

typing 모듈을 사용했을 때

from typing import Union

def add_numbers(x: Union[int, float], y: Union[int, float]) -> float:
    """
    두 숫자의 합을 계산하여 반환합니다.

    Parameters:
    x (Union[int, float]): 첫 번째 숫자. 정수 또는 부동소수점 수가 될 수 있습니다.
    y (Union[int, float]): 두 번째 숫자. 정수 또는 부동소수점 수가 될 수 있습니다.

    Returns:
    float: `x`와 `y`의 합.

    Examples:
    >>> add_numbers(1, 2)
    3
    >>> add_numbers(1.5, 2.3)
    3.8

    Notes:
    이 함수는 `int` 또는 `float` 타입의 두 입력값을 받아 그 합을 `float`로 반환합니다.
    """
    return x + y

 

 

728x90
728x90

파이썬에서 사용하는 PEP(Program Enhance Proposal) 에서 Name Convention에 대해 알아보려고 한다. PE
P는 파이썬 개발자들 간의 관습을 적어놓은 것이라고 생깍하면 된다. 파이썬 유저라면 파이썬 코드를 작성할 때 PEP-8을 이용해서 쓰는 것을 추천한다.

목차

  1. 피해야 할 이름
  2. Package and Module Names (패키지 모듈 이름)
  3. Class Name (클래스 이름)
  4. Type Variable Names (타입 변수 이름)
  5. Exception Names (예외 이름)
  6. Global Variable Names (전역 변수 이름)
  7. Function and Variable Names (함수, 변수 이름)
  8. Method Names and Instance Variables (메소드와 인스턴스 변수 이름)
  9. Constants (상수) 

 

피해야 할 이름


‘l’ (소문자 엘), ‘O’ (대문자 오), 또는 ‘I’ (대문자 아이)와 같은 문자를 단일 문자 변수 이름으로 사용하지 말아햐 한다. 그 이유는 특정 글꼴에서는 숫자 일(1)과 숫자 영(0)이 알파벳과 구분이 가지 않기 때문이다. 대문자 아이 ('I') 대신에 대문자 엘 ('L')을 사용 할 것을 권장한다.

# Wrong
I = 3 
O = 'a'

# Correct
L = 5

 

패키지, 모듈 이름


모듈은 짧고 모두 소문자로 된 이름을 가져야 한다. 가독성을 향상시키면 모듈 이름에 밑줄을 사용할 수 있다. 파이썬 패키지도 짧고 모두 소문자로 된 이름을 가져야 하며, 밑줄 사용은 권장되지 않는다.

 

  • 모듈 이름이 짧고 소문자로 구성된 예시: numpy, requests, flask
  • 가독성을 위해 밑줄이 포함된 모듈 이름 예시: date_utils, file_parser

 

 

클래스 이름


클래스 이름은 일반적으로 Pascal Case 규칙을 사용한다. 

  • Pascal Case를 사용한 클래스 이름 예시 : DataLoader, GameCharacter

 

타입 변수 이름


 PEP 484에서 일반적으로 Pascal Case를 사용하며 짧은 이름을 사용하도록 권장한다. 

짧은 이름 + Pascal Case를 사용한 예시

T = TypeVar('T')
AnyStr = TypeVar('AnyStr')
Num = TypeVar('Num')

 

예외 이름


예외 클래스 이름은 클래스 + "Error"의 접미사를 붙여서 작성한다.

예시

  • ConnectionError
  • validationError
  • TimeoutError

 

전역 변수 이름


모든 문자를 대문자로 사용하며, 단어간의 구분은 하이픈( '-' )을 이용하여 작성합니다.

예시

  • MAX_SIZE
  • DEFAULT_TIMEOUT

 

함수, 변수 이름


함수 이름은 소문자로 해야하며, 필요에 따라 단어 사이의 밑줄을 사용한다. 이것은 Snake Case라고 생각하면 된다. 

예시

  • 함수 이름 예시 : calculate_tax., send_message
  • 변수 이름 예시 : item_count, database_connection

 

메소드 이름 및 인스턴스 변수


필요에 따라 단어를 구분하기 위해 밑줄을 사용하여 소문자로 작성해야 한다. 비공개 메소드와 인스턴스 변수에는 하이픈을 하나만 사용한다. 그리고 외부에서 접근을 못하게 하기 위해서는 언더바 2개를 사용한다. 두개 모두 Snake Case를 사용한다.

예시

  • 메소드 : calculate_total
  • 인스턴스 변수
    • user_id: 사용자의 ID를 저장하는 변수
    • _password: 비밀번호를 비공개로 저장하는 변수
    • __internal_cache: 클래스 내부에서만 사용되고 하위 클래스에서 이름 충돌을 방지하기 위한 비공개 변수

언더바 하나의 예시

class Account:
    def __init__(self, owner):
        self._balance = 0  # 내부 사용을 위한 변수로, 외부에서 직접 접근을 권장하지 않음
        self.owner = owner

    def deposit(self, amount):
        if amount > 0:
            self._balance += amount
            self._update_ledger(amount)  # 내부 메소드 호출

    def _update_ledger(self, amount):
        print(f"{amount}가 입금됐습니다.")

언더바 두 개의 예시

class Account:
    def __init__(self, owner):
        self.__balance = 0  # 이름 변경을 통해 클래스 외부에서 접근하기 어려운 변수
        self.owner = owner

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            self.__update_ledger(amount)  # 이름 변경을 통해 클래스 외부에서 접근하기 어려운 메소드

    def __update_ledger(self, amount):
        print(f"{amount}가 입금됐습니다.")

# 외부에서 접근 시도
account = Account("John")
print(account.__balance)  # AttributeError 발생

 

상수


전역 변수와 동일하게 모든 문자를 대문자로 사용하며, 단어간의 구분은 하이픈( '-' )을 이용하여 작성합니다.

예시

  • MAX_SIZE
  • DEFAULT_TIMEOUT

 

출처 : https://peps.python.org/pep-0008/#naming-conventions

728x90
728x90

Snake, Camel

프로그래밍을 하다 보면 변수, 함수, 클래스 등을 정의할 때 대문자를 쓰거나 언더바('_')를 쓰는 경우를 볼 수 있는데 이에 대해 자세하게 알아보려고 한다. 

요약

명명 규칙 설명 예시
Snake_case 모든 글자를 소문자로 하며, 단어는 밑줄로 구분한다. new_object, my_website_name, user_profile
camelCase PascalCase와 유사하지만 첫 글자는 소문자이며, 공백은 없다. newObject, myWebsiteName, userProfile
PascalCase 각 단어의 첫 글자를 대문자로 이며, 공백은 없다 NewObject, MyWebsiteName, UserProfile
Kebab-case 모든 글자를 소문자로 하며, 단어는 하이픈( '-' )으로 구분한다. new-object, my-website-name, user-profile

 

각각 언어가 선호하는 Case

언어 선호하는 케이스 예시
Python Snake Case Class: Pascal Case (MyClass)

Others: snake case (my_variable)
Java Camel Case Class, Interface : Pascal Case

Others: Camel Case
JavaScript Camel Case Class: Pascal Case (MyComponent)

Others: Camel Case (myFunction, myVariable)
C Snake Case Functions and Variables: Snake Case (my_variable, calculate_value)
C++ Mixed Class, Struct: Pascal Case (MyClass, MyStruct)

Functions and Variables: Camel Case (myFunction, myVariable)
C# Camel Case Class, Interface: Pascal Case (MyClass, IMyInterface)

Others: Camel Case (myVariable, CalculateValue)
Ruby Snake Case Class, Module: Pascal Case (MyClass, MyModule)

Others: snake case (my_variable, calculate_value)
React Camel Case Components: Pascal Case (MyComponent)

Others: Camel Case (myVariable, handleEvent)
Go Camel Case Struct, Interface: Pascal Case (MyStruct, MyInterface)

Others: Camel Case (myVariable, ProcessData)
Swift Camel Case Class, Enum, Struct,: Pascal Case (MyClass, MyEnum, MyStruct)

Others: Camel Full (myVariable, calculateValue)
  • * Exception, Class를 지원하는 언어의 경우는 Pascal Case를 사용한다.
  • * 상수(Constant)의 경우 대문자와 밑줄로 작성한다. (MAX_NUM)

 

Snake_case


Snake case는 모든 단어를 소문자로 쓰고, 단어 사이를 밑줄('_')로 연결하는 명명 방식이다. 이 스타일은 주로 파이썬과 같은 프로그래밍 언어에서 변수와 함수 이름에 사용된다. 변수의 형태가 뱀과 같다는 뜻에서 이름이 붙었다. 주로 파이썬과 같은 프로그래밍 언어에서 사용되는 규칙이다. 장점은 가독성이 좋아진다. 

예시

  • my_variable_name
  • calculate_total_sum()

 

camelCase


Camel case는 단어의 첫 글자를 제외하고 각 단어의 첫 글자를 대문자로 표기하여 단어를 구분하는 명명 방식이다. 이 방식은  낙타의 등과 비슷하다고 해서 Camel Case라고 불린다.

Camel Case는 크게 두 가지 유형으로 나뉜다: Upper Camel Case (Pascal Case)와 Lower Camel Case. 여기서 보통 Camel Case라고 하면, Lower Camel Case를 의미하며 Upper Cemel Case는 Pascal Case로 불리게 된다.

예시

  • Upper Camel Case(Pascal Case) : ClassName
  • Lower Camel Case : methodName

 

PascalCase


Pascal Case는 단어의 첫 글자를 대문자로 시작하고, 나머지 글자를 소문자로 표기하는 명명 방식이다. 이 방식은 Pascal 언어에서 사용해서 이름이 붙여졌다. 이 규칙은 공백, 밑줄과 하이픈을 사용하지 않는다. 그래서 대분자로 단어를 구분한다. 특히 자바에서 많이 사용한다.

* 언어를 고사하고 Class 정의는 Pascal Case를 사용한다.

예시

  • CustomerService

 

Kebab-case


Kebab Case는 모든 글자를 소문자로 쓰고 각 단어를 하이픈으로 표기하는 명명 방식이다. 특히 HTML, CSS, URL, 파일 이름 등 웹 기술에서 자주 사용되는 언어이다.

예시

  • URL : https://example.com/my-page
  • CSS : menu-item
  • File Name : my-document.txt
728x90
728x90

오늘은 yaml과 yml 파일 확장자가 무엇인지 알아보려고 한다. 그 이유는 docker-compose 파일과 스키마 작성시 yaml 확장자 파일을 사용하는데 내가 이전에는 본적이 없던 파일 형식이지만, 자주 사용되는 것을 보게 되었고 이에 대해 공부했던 내용들을 정리하려고 한다.

 

YAML, YML은 무엇인가?


yaml( YAML Ain't Markup Language)는 데이터를 표현하는 방법 중에 하나이다. JSON이나 XML와 비슷한 형식으로 작성할 수 있어서 자주 사용하는 방식입니다. 마크업 언어보다 데이터 중심을 구성을 강조한다는 점이 있으며, 공백문자를 이용한 들여쓰기로 파일을 작성한다. 파일 형식은 Key-Value 형식으로 작성한다.

 

YAML, YML의 차이


YML은 YAML의 파일 확장자 중 하나입니다. .yaml과 .yml 확장자는 기능적으로 동일하며, 둘 다 YAML 데이터 형식을 나타냅니다. .yaml 확장자는 좀 더 명확하게 YAML 파일임을 나타내지만, .yml은 더 짧고 간결하다 정도로 알면 된다.

 

YAML, YML 파일은 왜 사용되는가?


YAML 파일은 사람이 읽기 쉬운 데이터 직렬화 형식으로, 구성 파일, 배포 스크립트, 데이터 저장 등에 용이하다. JSON에 비해 더 읽기 쉽고, 중괄호와 쉼표의 사용이 적어서 데이터를 설명하거나 문서화하기 쉽습니다. YAML은 데이터의 계층적 관계를 나타내기 위해 공백(들여쓰기)을 사용하여 구조를 명확하게 표현할 수 있다. 

 

YAML, YML 파일은 어디에 사용되는가?


YAML 파일은 다양한 개발 및 운영 환경에서 널리 사용된다. 내가 사용한 경우는 다음과 같다.

  1. Cloud Sevice : docker-compose 파일은 docker-compose.yaml 파일 형식으로 작성하며 각각의 컨테이너에 대한 정보를 정의할 때 사용한다.
  2. FastAPI Schema : BackEnd System에서 요청과 응답에 대해 정의를 해야할 때 사용한다.

내가 사용한 예시는 극히 일부이며 다양한 곳에서 사용되는데 특히 무언가를 정의할 때 많이 사용한다.

 

YAML, YML 정의


yaml를 작성하는 방법은 다음과 같다.

  • 들여쓰기(띄어쓰기 2번 or Tab 1번)를 이용해서 작성
  • Key-Value 형태로 작성하며, Key와 Value 사이에는 콜론( : )을 사용하여 구분

 

yaml에서 사용하는 데이터 타입은 다음과 같다.

  • 문자열 : 작은 따옴표나 큰 따옴표 상관없이 작성 
  • 숫자 : 정수, 소수, 16진수 표현 
  • 불리언 : true, false로 표현 
  • 리스트 : 하이픈 ( - )을 이용하여 리스트 나열
  • 맵 : Key-Value를 표현
  • 널 : nulll로 표현

 

다음은 각 자료형을 작성하는 예시이다.

# 문자열
name: "John Doe"            # 따옴표 사용
description: 'He\'s a developer at "Big Tech".'  # 특수 문자 포함 문자열

# 숫자
age: 30                    # 정수
salary: 56000.50           # 부동 소수점
hex_value: 0x1A3F          # 16진수

# 불리언
active: true               # 참 값
retired: false             # 거짓 값

# 리스트
languages:                 # 아이템 목록
  - Python
  - JavaScript
  - Rust

# 맵
address:
  street: "1234 Main St"
  city: "Anytown"
  state: "CA"
  zipcode: 90210

# 널
children: null             # null 값 사용

 

728x90
728x90

오류


> pip
Fatal error in launcher: Unable to create process using

pip 오류의 발생원인은 다음과 같다.

  • 환경 변수 문제: PATH 환경 변수에 Python 설치 경로가 정확하게 설정되지 않았을 수 있다.
  • 버전 충돌: 사용하는 Python 버전과 설치된 패키지 버전이 서로 호환되지 않을 수 있다.

 

해결방법


# pip 재설치
python -m pip install --upgrade --force-reinstall pip

 

해결방법은 pip를 재설치하면 된다. 

728x90