공식문서

source code

math

math.gcd

math.gcd() 함수를 이용하면 최대공약수(gcd, greatest common divisor)를 구할 수 있습니다.
math.gcd() 함수는 python3.5부터 사용할 수 있습니다.

import math 
gcd = math.gcd(50, 100, 70)
print(gcd)  # 10

위 코드처럼 50과 100, 70의 최대 공약수를 구할 수 있습니다.

math.lcm

math.lcm()은 최소공배수(lcm, least common multiple)를 구하는 함수입니다.
math.lcm()은 python3.9부터 사용할 수 있습니다.

import math
lcm = math.lcm(25, 40)
print(lcm)  # 200

위 코드처럼 25와 40의 최소 공배수를 구할 수 있습니다.

 

 

 

'Python > Python' 카테고리의 다른 글

Python 라이브러리 - fractions  (0) 2023.05.17
Python 라이브러리 - decimal  (0) 2023.05.16
Python 라이브러리 - enum  (0) 2023.05.16
Python 라이브러리 - pprint  (0) 2023.05.16
Python 라이브러리 - heapq  (0) 2023.05.16

공식 문서

source code

enum

enum은 서로 관련이 있는 여러 개의 상수 집합을 정의할 때 사용하는 모듈입니다.
enum 모듈은 python3.4 버전 부터 사용할 수 있습니다.

만약 날짜를 입력하면 그 날의 요일에 해당하는 메뉴를 반환하는 get_menu() 함수를 아래처럼 만들었습니다.

from datetime import date 


def get_menu(input_date: date) -> str:
    weekday = input_date.isoweekday()       # 1: 월요일, 2: 화요일, ..., 7: 일요일
    if weekday == 1:
        menu = "김치찌개"
    elif weekday == 2:
        menu = "부대찌개"
    elif weekday == 3:
        menu = "순두부찌개"
    elif weekday == 4:
        menu = "스테이크"
    elif weekday == 5:
        menu = "치킨"
    elif weekday == 6:
        menu = "스프"
    elif weekday == 7:
        menu = "라면"
    return menu


print(get_menu(date(2023, 3, 2)))   # 스테이크

프로그래밍에서 상수로 선언하지 않은 숫자를 매직넘버라고 합니다.

위 프로그램에서도 1~7이라는 매직 넘버를 사용해 요일을 나타냈습니다. 이러한 매직넘버를 사용하면 코드의 가독성이 떨어집니다. 이 문제를 개선하기 위해서는 enum 라이브러리를 사용할 수 있습니다.

from datetime import date
from enum import IntEnum


class Week(IntEnum):
    MONDAY = 1
    TUESDAY = 2
    WEDNESDAY = 3
    THURSDAY = 4
    FRIDAY = 5
    SATURDAY = 6
    SUNDAY = 7


def get_menu(input_date: date) -> str:
    menu = {
        Week.MONDAY: "김치찌개",
        Week.TUESDAY: "라면",
        Week.WEDNESDAY: "부대찌개",
        Week.THURSDAY: "스프",
        Week.FRIDAY: "치킨",
        Week.SATURDAY: "스테이크",
        Week.SUNDAY: "삼겹살",
    }
    return menu[input_date.isoweekday()]


print(get_menu(date(2023, 3, 23)))  # 스프

Week 클래스는 enum.IntEnum 을 상송하여 만든 Enum 자료형 입니다. 이렇게 Enum 자료형을 만들어 상수로 사용하면 유지보수에 유리하고 가독성이 좋아집니다.

print(Week.MONDAY.name)  # MONDAY
print(Week.MONDAY.value)  # 1

이렇게 Enum 자료형은 name과 value 속성으로 접근할 수 있습니다.

for week in Week:
    print("{}:{}".format(week.name, week.value))
# MONDAY:1
# TUESDAY:2
# WEDNESDAY:3
# THURSDAY:4
# FRIDAY:5
# SATURDAY:6
# SUNDAY:7

for 문으로도 반복 할 수 있습니다.

 

 

'Python > Python' 카테고리의 다른 글

Python 라이브러리 - decimal  (0) 2023.05.16
Python 라이브러리 - math  (0) 2023.05.16
Python 라이브러리 - pprint  (0) 2023.05.16
Python 라이브러리 - heapq  (0) 2023.05.16
Python 라이브러리 - collections  (0) 2023.05.11

공식 문서

source code

pprint는 데이터를 보기 좋게 출력(pretty print)할 때 사용하는 모듈입니다.

import pprint

result = {'userId': 1, 'id': 1, 'title': 'hi', 'body': 'https://docs.python.org/ko/3/library/pprint.html'}
pprint.pprint(result)
# {'body': 'https://docs.python.org/ko/3/library/pprint.html',
#  'id': 1,
#  'title': 'hi',
#  'userId': 1}

pprint는 주로 구조가 복잡한 JSON 데이터를 디버깅 용도로 출력할 때 사용합니다.

 

 

 

 

'Python > Python' 카테고리의 다른 글

Python 라이브러리 - math  (0) 2023.05.16
Python 라이브러리 - enum  (0) 2023.05.16
Python 라이브러리 - heapq  (0) 2023.05.16
Python 라이브러리 - collections  (0) 2023.05.11
Python 라이브러리 - calendar  (0) 2023.05.11

공식 문서

source code

heapq는 순위가 가장 높은 자료(data)를 가장 먼저 꺼내는 우선순위 큐를 구현한 모듈입니다.

만약 기록이 좋은 순서대로 3명을 자동으로 뽑는 프로그램을 아래처럼 구현할 수 있습니다.

import heapq

data = [
    (19, 'lee'),
    (20, 'park'),
    (12, 'kim'),
    (30, 'cho'),
    (13, 'ki'),
    (43, 'choi'),
]

h = []      # 힙 생성
for score in data:
    heapq.heappush(h, score)    # 힙에 데이터 저장

for i in range(3):
    print(heapq.heappop(h))     # 최솟값부터 힙 반환
# (12, 'kim')
# (13, 'ki')
# (19, 'lee')

heappush()로 튜풀을 추가할 때는 데이터의 우선순위를 나타내는 항목이 첫 번째여야 합니다. 따라서 기록과 이름을 쌍으로 하는 튜플은 (기록, 이름)으로 구성해야 합니다.

이렇게 힙을 구성하면 heapq.heappop()을 이용하여 우선순위 대로 값을 꺼낼 수 있습니다.

heapq.heapify(data)

for i in range(2):
    print(heapq.heappop(data))
# (12, 'kim')
# (13, 'ki')

heapify() 함수를 사용해 간단하게 구현할 수 있습니다. 이때는 data 리스트가 힙 구조에 맞게 변경됩니다.

print(heapq.nsmallest(4, data))
# [(19, 'lee'), (20, 'park'), (30, 'cho'), (43, 'choi')]

nsmallest() 함수를 사용하면 더욱 간단합니다.
heapq.nsmallest(n, iterable)은 반복 가능한 객체(iterable) 데이터 집합에서 n개의 가장 작은 요소로 구성된 리스트를 반환합니다.

 

 

'Python > Python' 카테고리의 다른 글

Python 라이브러리 - enum  (0) 2023.05.16
Python 라이브러리 - pprint  (0) 2023.05.16
Python 라이브러리 - collections  (0) 2023.05.11
Python 라이브러리 - calendar  (0) 2023.05.11
Python 라이브러리 - datetime  (0) 2023.05.11

collections

source code

공식 문서

deque

deque는 앞과 뒤에서 데이터를 처리할 수 있는 양방향 자료형입니다. 스택(stack)처러 사용해도 되고, 큐(queue)처럼 사용해도 됩니다.

deque 사용법

deque는 list와 매우 비슷합니다. 스택과 큐로 활용할 수 있는 메서드도 대부분 일치합니다.

다만, deque에는 아래와 같은 메서드들이 더 있습니다.

  • appendleft(x) : 데크 왼쪽에 x 추가
  • popleft() : 데크 왼쪽에서 요소 제거
    물론 list에서도 pop(0)을 사용해서 첫 번째 요소를 제거할 수 있지만 deque를 사용하면 아래와 같은 장점이 있습니다.
  • deque는 list보다 속도가 빠릅니다. pop(0)을 수행하려면 O(N)연산을 수행하지만, popleft() 연산을 수행하면 O(1) 연산을 수행합니다.
  • 스레드 환경에서 안전합니다.
d = deque([1, 2, 3, 4, 5])
d.append(6)
d.appendleft(0)
print(d)        # deque([0, 1, 2, 3, 4, 5, 6])
d.pop()
d.popleft
print(d)        # deque([0, 1, 2, 3, 4, 5])

리스트를 n만큼 회전하는 문제는 collections.deque 모듈을 사용하면 간단하게 해결할 수 있습니다.

from collections import deque

list_a = [1, 2, 3, 4, 5]
q = deque(list_a)
q.rotate(2)     # 시계 방향 회전은 양수, 그 반대는 음수
result = list(q)
print(result)   # [4, 5, 1, 2, 3]

deque(list_a)로 deque 객체를 만든 후 rotate() 함수를 사용해 2만큼 오른쪽으로 회전하면 첫 값이 4를 가리키게 됩니다.

namedtuple

튜플(tuple)은 인덱스를 통해서만 데이터에 접근이 가능하지만, 네임드 튜플(namedtuple)은 인덱스뿐만 아니라 key로도 데이터에 접근할 수 있는 자료형입니다.

from collections import namedtuple

data = [
    ('kim', 30, '01012341234'),
    ('lee', 10, '01012331233'),
    ('park', 20, '01012221222'),
]

Employee = namedtuple(typename='Employee', field_names='name, age, cellphone')

# data의 각 튜플을 모두 Employee자료형(namedtuple자료형)으로 교체
# 리스트 컴프리헨션 이용한 경우
data = [Employee(name=emp[0], age=emp[1], cellphone=emp[2]) for emp in data]
# _make() 함수 사용한 경우
data = [Employee._make(emp) for emp in data]

emp = data[0]       # 첫 번째 직원
print(emp.name)     # kim
print(emp[0])       # kim   (인덱스로도 접근 가능)
print(emp.age)      # 30
print(emp.cellphone)# 01012341234

# 네임드 튜플은 값을 변경할 수 없는(immutable) 튜플의 특징을 그대로 가지고 있어서 속성 값을 변경할 수 없다.
# emp.name = 'cho'
# 다만, _replace() 함수를 사용하면 값을 변경할 수 있다.
new_emp = emp._replace(name="cho")
print(new_emp)      # Employee(name='cho', age=30, cellphone='01012341234')
# 단, _replace() 함수는 객체를 직접 변경하는 것이 아니라 값을 변경한 새로운 객체를 만들어 반환한다.

# _asdict() 함수를 사용해 딕셔너리로 변환
emp_dict = emp._asdict()
print(emp_dict)     # {'name': 'kim', 'age': 30, 'cellphone': '01012341234'}

Counter

collections.Counter는 리스트나 문자열과 같은 자료형의 요소 중 값이 같은 요소가 몇 개인지를 확인할 때 사용하는 클래스입니다.

아래의 코드는 시에서 가장 많이 사용한 단어와 그 개수를 구하려는 코드입니다.

from collections import Counter
import re 

data = """
나 보기가 역겨워
가실 때에는
말없이 고이 보내 드리우리다
영변에 약산
진달래 꽃
아름 따다 가실 길에 뿌리우리다
가시는 걸음걸음
놓인 그 꽃을
사뿐히 즈려밟고 가시옵소서
나 보기가 역겨워
가실 때에는
죽어도 아니 눈물 흘리우리다
"""

# 문장을 단어별로 나누고자 re 모듈 사용
words = re.findall(r'\w+', data)    # r: 정규표현식이 원시 문자열(raw string)임을 알려주는 문자
# \w+ : 단어를 의미
# re.findall() 함수를 이용하여 모든 단어를 리스트(words)로 반환
counter = Counter(words)
# Counter 클래스의 객체 counter 생성
print(counter)
# Counter({'가실': 3, '나': 2, '보기가': 2, '역겨워': 2, '때에는': 2, '말없이': 1, '고이': 1, '보내': 1, '드리우리다': 1, '영변에': 1, '약산': 1, '진달래': 1, '꽃': 1, '아름': 1, '따다': 1, '길에': 1, '뿌리우리다': 1, '가시는': 1, '걸음걸음': 1, '놓인': 1, '그': 1, '꽃을': 1, '사뿐히': 1, '즈려밟고': 1, '가시옵소서': 1, '죽어도': 1, '아니': 1, '눈물': 1, '흘리우리다': 1})
# 단어 빈도수가 큰 것부터 차례대로 출력
print(counter.most_common(1))
# [('가실', 3)]
# Counter 객체의 most_common() 함수를 이용해 빈도수가 가장 많은 1개의 단어 출력

# most_common() 함수는 빈도수가 많은 것부터 인수로 입력한 개수만큼 튜플로 반환
print(counter.most_common(2))
# [('가실', 3), ('나', 2)]

defaultdict

collections.defaultdict는 값(value)에 초깃값을 지정하여 딕셔너리를 생성하는 모듈

만약 문자열에서 사용한 문자(key)와 해당 문자의 사용 횟수(value)를 딕셔너리로 만든다면 일반적으로 아래 코드처럼 작성할 수 있습니다.

text = "Life is too short, You need Python."

d = dict()
for key in text:
    if key not in d:
        d[key] = 0
    d[key] += 1

print(d)
# {'L': 1, 'i': 2, 'f': 1, 'e': 3, ' ': 6, 's': 2, 't': 3, 'o': 5, 'h': 2, 'r': 1, ',': 1, 'Y': 1, 'u': 1, 'n': 2, 'd': 1, 'P': 1, 'y': 1, '.': 1}

위의 코드의 경우 딕셔너리 d의 키에 해당 문자가 없다면 그 문자를 키로 등록하고 값은 0으로 초기화하는 방어적인 코드가 존재해야 합니다.

# 방어적인 코드
if key not in d:
    d[key] = 0

만약 방어적인 코드가 없다면 딕셔너리에 해당 키 값이 없는 상태에서 += 연산을 수행하므로 에러가 발생할 것입니다.

for key in text:
    d[key] += 1

딕셔너리로 이와 같은 집계용 코드를 작성할 때는 항상 초깃값에 신경을 써야 합니다. 하지만 collections의 defaultdict를 사용하면 이러한 번거로움을 피할 수 있습니다.

from collections import defaultdict

text = "Life is too short, You need Python."

d = defaultdict(int)
for key in text:
    d[key] += 1

print(d)
# defaultdict(<class 'int'>, {'L': 1, 'i': 2, 'f': 1, 'e': 3, ' ': 6, 's': 2, 't': 3, 'o': 5, 'h': 2, 'r': 1, ',': 1, 'Y': 1, 'u': 1, 'n': 2, 'd': 1, 'P': 1, 'y': 1, '.': 1})
print(dict(d))
# {'L': 1, 'i': 2, 'f': 1, 'e': 3, ' ': 6, 's': 2, 't': 3, 'o': 5, 'h': 2, 'r': 1, ',': 1, 'Y': 1, 'u': 1, 'n': 2, 'd': 1, 'P': 1, 'y': 1, '.': 1}

defaultdict()의 인수로 int를 전달하여 딕셔너리 d를 생성합니다. int를 기준으로 생성한 딕셔너리 d의 값은 항상 0으로 자동 초기화됩니다.

 

 

 

 

'Python > Python' 카테고리의 다른 글

Python 라이브러리 - pprint  (0) 2023.05.16
Python 라이브러리 - heapq  (0) 2023.05.16
Python 라이브러리 - calendar  (0) 2023.05.11
Python 라이브러리 - datetime  (0) 2023.05.11
Python 라이브러리 - re  (0) 2023.05.11

calendar

source code

공식 문서

isleap()

calendar.isleap()은 인수로 입력한 연도가 윤년인지를 확인할 때 사용하는 함수입니다.

참고 - 윤년 규칙

  • 서력기원 연수가 4로 나누어 떨어지는 해는 우선 윤년으로 한다.
  • 그중 100으로 나누어 떨어지는 해는 평년으로 한다.
  • 400으로 나누어 떨어지는 해는 다시 윤년으로 정한다.

윤년을 계산하는 사용자 정의 함수

def is_leap_year(year):
    if year % 400 == 0:
        return True 
    if year % 100 == 0:
        return False
    if year % 4 == 0:
        return True 
    return False

isleap() 함수 활용

import calendar
print(calendar.isleap(2023))        # False

 

 

 

 

'Python > Python' 카테고리의 다른 글

Python 라이브러리 - heapq  (0) 2023.05.16
Python 라이브러리 - collections  (0) 2023.05.11
Python 라이브러리 - datetime  (0) 2023.05.11
Python 라이브러리 - re  (0) 2023.05.11
Python 라이브러리 - textwrap  (1) 2023.05.10

datetime 모듈

source code

공식 문서 링크

관련 자료

date

datetime.date 모듈은 년, 월, 일로 날짜를 표현할 때 사용하는 모듈이다.

import datetime

# date
day1 = datetime.date(2023, 4, 10)
print(day1)     # 2023-04-10

이렇게 datetime.date 객체를 만들 수 있습니다.

day2 = datetime.date(2023, 8, 14)

diff = day2 - day1
print(diff)     # 126 days, 0:00:00

date 객체 간의 연산이 가능하고 연산 결과는 datetime.timedelta 객체가 반환됩니다.

datetime

시, 분, 초까지 포함한 데이터는 아래처럼 datetime.datetime을 사용합니다.

day3 = datetime.datetime(2023, 4, 1, 13, 10, 50)
print(day3)     # 2023-04-01 13:10:50

combine()

combine() 함수로 datetime.date 객체와 datetime.time 객체를 합쳐 데이터를 만들 수도 있습니다.

day = datetime.date(2023, 4, 5)
time = datetime.time(9, 29, 30)
dt = datetime.datetime.combine(day, time)
print(dt)       # 2023-04-05 09:29:30

timedelta

datetime.timedelta()는 두 날짜의 차이를 계산할 때 사용하는 함수입니다. 산술 연산자 +와 -를 사용할 수 있습니다.

# 100일 후 연산
today = datetime.date.today()
print(today)        # 2023-05-06

diff_days = datetime.timedelta(days=100)
print(diff_days)        # 100 days, 0:00:00

print(today + diff_days)    # 2023-08-14

 

 

 

 

'Python > Python' 카테고리의 다른 글

Python 라이브러리 - collections  (0) 2023.05.11
Python 라이브러리 - calendar  (0) 2023.05.11
Python 라이브러리 - re  (0) 2023.05.11
Python 라이브러리 - textwrap  (1) 2023.05.10
Python에서의 캡슐화  (0) 2023.04.28

source code

공식 문서 링크

정규 표현식 관련 링크

정규표현식(Regular Expressions)

정규 표현식이란 복잡한 문자열을 처리할 때 사용하는 기법으로 파이썬, C, java 등 문자열을 처리해야 하는 다양한 곳에서 활용할 수 있다.
파이썬에서 정규표현식을 이용하려면 re 모듈을 사용한다.

만약 주민등록 번호 뒷자리를 *로 바꾸는 프로그램을 작성한다면 아래와 같이 작성할 수 있다.

data = """
안녕하세요 저는 이윤태입니다.
제 주민등록 번호는 970701-1234567
만약 다음 8월에 태어났다면 970801-1234567 겠네요.
"""

result = []
for line in data.split("\n"):
    word_result = []
    for word in line.split(" "):
        if len(word) == 14 and word[:6].isdigit() and word[7:].isdigit():
            word = word[:6] + "-" + "*******"
        word_result.append(word)
    result.append(" ".join(word_result))
print("\n".join(result))
# 안녕하세요 저는 이윤태입니다.
# 제 주민등록 번호는 970701-*******
# 만약 다음 8월에 태어났다면 970801-******* 겠네요.

re 모듈

정규표현식과 re 모듈을 사용하면 아래처럼 더 간단하게 작성할 수 있다.

import re

pat = re.compile("(\d{6})[-](\d{7})")
print(pat.sub("\g<1>-*******", data))
# 안녕하세요 저는 이윤태입니다.
# 제 주민등록 번호는 970701-*******
# 만약 다음 8월에 태어났다면 970801-******* 겠네요.

 

 

 

'Python > Python' 카테고리의 다른 글

Python 라이브러리 - calendar  (0) 2023.05.11
Python 라이브러리 - datetime  (0) 2023.05.11
Python 라이브러리 - textwrap  (1) 2023.05.10
Python에서의 캡슐화  (0) 2023.04.28
Subtype in Python  (0) 2023.03.30

textwrap

공식 문서 링크

source code

shorten

문자열을 원하는 길이에 맞게 줄여 표시

import textwrap

# shorten
text = "life is too short, you need python"

result = textwrap.shorten(text=text, width=15)
print(result)   # life is [...]

문자열에 포함된 모든 연속 공백은 하나의 공백 문자로 줄어든다. 축약된 문자열임을 뜻하는 [...] 역시 전체 길이에 포함되며 문자열은 단어 단위로 길이에 맞게 줄어든다.

한글도 마찬가지로 적용된다. 단, 한글 1 문자를 길이 2가 아닌 1로 계산한다.

text = "인생은 짧으니 파이썬이 필요해"

result = textwrap.shorten(text=text, width=15)
print(result)   # 인생은 짧으니 [...]

축약 표시 [...]를 다른 문자로 변경하고 싶으면 매개변수 placeholder를 이용한다.

result = textwrap.shorten(text=text, width=15, placeholder='...')
print(result)   # 인생은 짧으니 파이썬이...

wrap

긴 문자열을 원하는 길이로 줄 바꿈(wrapping)할 때 사용하는 함수

long_text = "life is too short, you need python. " * 10

result = textwrap.wrap(text=long_text, width=50)
print(result)   
# ['life is too short, you need python. life is too', 'short, you need python. life is too short, you', 'need python. life is too short, you need python.', 'life is too short, you need python. life is too', 'short, you need python. life is too short, you', 'need python. life is too short, you need python.', 'life is too short, you need python. life is too', 'short, you need python.']

wrap() 함수는 문자열을 width 길이만큼 자르고 이를 리스트로 반환합니다.

print('\n'.join(result))
"""
life is too short, you need python. life is too
short, you need python. life is too short, you
need python. life is too short, you need python.
life is too short, you need python. life is too
short, you need python. life is too short, you
need python. life is too short, you need python.
life is too short, you need python. life is too
short, you need python.
"""

이처럼 join() 함수로 문자열 사이에 줄 바꿈 문자(\n)를 넣어 하나로 합친 다음 출력할 수 있습니다.

fill()

fill()메서드를 사용하면 위의 과정을 한 번으로 줄일 수 있습니다.

result = textwrap.fill(text=long_text, width=70)
print(result)
"""
life is too short, you need python. life is too
short, you need python. life is too short, you
need python. life is too short, you need python.
life is too short, you need python. life is too
short, you need python. life is too short, you
need python. life is too short, you need python.
life is too short, you need python. life is too
short, you need python.
"""

 

 


 

http://www.yes24.com/Product/Goods/109209932

 

Do it! 점프 투 파이썬 라이브러리 예제 편 - YES24

『Do it! 점프 투 파이썬』의 후속 편 출간!다양한 예제로 배우는 파이썬 라이브러리 실전 안내서!이 책은 『Do it! 점프 투 파이썬』의 박응용 저자가 그동안 수많은 독자에게 받았던 ‘이제 무엇

www.yes24.com

Do it! 점프 투 파이썬 라이브러리 예제 편⌟ 책을 공부하며 요약・정리한 내용입니다.


'Python > Python' 카테고리의 다른 글

Python 라이브러리 - datetime  (0) 2023.05.11
Python 라이브러리 - re  (0) 2023.05.11
Python에서의 캡슐화  (0) 2023.04.28
Subtype in Python  (0) 2023.03.30
First Class Function  (0) 2023.03.30

Python에서의 캡슐화

Python 캡슐화는 객체의 내부 세부 정보를 숨기고 필요한 정보와 기능만 노출하는 방법입니다.

캡슐화는 클래스 특성 및 메서드의 가시성을 제어하는 특수 키워드인 access modifiers를 사용합니다.

Python에서의 3가지 access modifiers

  • Public : Public 속성과 메서드는 클래스 내부, 외부 어디에서나 접근할 수 있습니다. Python에서는 모든 속성과 메소드가 기본적으로 공개됩니다.
  • Protected : Protected 속성과 메서드는 하나의 언더바(_)로 표시합니다. 클래스 및 하위 클래스 내에서는 접근할 수 있지만, 클래스 계층 구조 외부에서는 접근할 수 없습니다.
  • Private : Private 속성과 메서드는 두 개의 언더바(__)로 표시합니다. 정의한 클래스 내에서만 접근할 수 있으며, 하위 클래스나 클래스 외부에서는 접근할 수 없습니다.

private 예제

source code

class BankAccount:
    def __init__(self, account_number: str, balance: int) -> None:
        self.__account_number: str = account_number
        self.__balance: int = balance

    def deposit(self, amount: int) -> None:
        self.__balance += amount

    def withdraw(self, amount: int) -> None:
        if amount <= self.__balance:
            self.__balance -= amount
        else:
            print("Insufficient funds")

    def get_balance(self) -> int:
        return self.__balance

# bank account 객체 생성
my_account = BankAccount("123456789", 1000)

# 계좌번호 속성에 직접 접근 시도(실패)
# print(my_account.__account_number)  # AttributeError: 'BankAccount' object has no attribute '__account_number'

# public method를 사용해 입금
my_account.deposit(500)

# 출금 불가능한 금액 출금 시도 (실패)
my_account.withdraw(2000)       # Insufficient funds

# public method를 활용해서 현재 잔액 get
print(my_account.get_balance())     # 1500

# 이렇게 접근 가능
print(my_account._BankAccount__account_number)   # 123456789

name mangling variable

__acount_number와 같이 언더 바가 앞에 2개인 변수를 name magling variable이라고 합니다.

아래 글에 name mangling에 대해서 매우 잘 정리해주셨습니다. 아래 글을 참고하시면 아래 내용 이해하는데 큰 도움이 되실겁니다.
name magling varialbe 관련 글

이러한 name magling 방식으로 변수를 선언하면 이 변수는 클래스에 private 변수가 됩니다.
그래서 위의 글 처럼 외부에서 접근이 어렵습니다.

다만, 접근이 어려울 뿐이지 외부에서 접근을 업격하게 제한하는 것은 아닙니다. 다시말해, 접근은 할 수 있습니다.(위의 글에서도 나오는 내용입니다.)

name mangling을 적용한 변수에 파이썬 인터프리터는 자동으로 변수 이름을 _classname__variable로 바꿉니다. 여기서 classname은 클래스 이름이고, variable은 변수 이름입니다.
위의 예시를 보면 __acount_number는 파이썬 인터프리터에 의해 _BankAccount__account_number로 바뀌는 것입니다. 그래서

print(my_account._BankAccount__account_number)   # 123456789

이 코드로 접근이 가능한 것입니다.

이처럼 name mangling variable은 파이썬의 관례일뿐이지, 여전히 변수에 엑세스 할 수 있습니다.

Protected 예제

soruce code

class MyClass:
    def __init__(self) -> None:
        self._protected_variable = 42


class MySubClass(MyClass):
    def __init__(self) -> None:
        super().__init__()
        print(self._protected_variable)     # 42


# 객체 생성
obj1 = MyClass()
obj2 = MySubClass()

# protected 변수 직접 접근
print(obj1._protected_variable)     # 42

# 외부에서 protected 변수 접근 시도
print(obj2._protected_variable)     # 42

위의 예시처럼 MyClass와 MySubClass 객체를 생성할 때 _protected_variable에 직접 엑세스 할 수 있습니다. 그러나 클래스 외부에서 이 변수를 접근할 경우는 에러가 발생합니다.

'Python > Python' 카테고리의 다른 글

Python 라이브러리 - re  (0) 2023.05.11
Python 라이브러리 - textwrap  (1) 2023.05.10
Subtype in Python  (0) 2023.03.30
First Class Function  (0) 2023.03.30
파이썬에서 Function과 method의 차이  (0) 2023.03.20