파이썬에서 __init__, self, super를 사용하여 클래스를 정의하는 방법을 알아보자.
# 클래스와 객체의 정의
클래스(Class): 객체를 만들기 위한 청사진 또는 설계도
객체(Object): 클래스를 사용하여 생성된 실제 인스턴스
이다. 클래스==붕어빵 틀, 객체==붕어빵이라는 비유를 어디선가 들었던 것 같다.
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
# Person 클래스의 인스턴스 생성
person1 = Person("Alice", 30)
person2 = Person("Bob", 25)
# __init__ 메서드
__init__ 메서드는 클래스의 생성자로, 객체가 생성될 때 자동으로 호출된다. 객체를 초기화하는 데 사용되며, 클래스 내부에서 반드시 필요한 변수들(각각의 인스턴스가 가지는 속성)을 설정한다. 이때 첫번째 매개변수는 무조건 self인데 self는 대체 뭘까?
# self 키워드
self는 클래스의 인스턴스 자신을 가리킨다. 메서드의 첫 번째 매개변수로 사용되며, self를 통해서 나중에 클래스 내부의 변수와 메서드에 접근할 수 있습니다.
위의 예제 코드를 보면, Person 클래스를 선언한 다음 그걸 이용해서 Person1, Person2라는 인스턴스를 생성한다.
생성할 때 매개변수 중 self는 생략하고, 각각의 인스턴스에 해당하는 속성들(이름, 나이)를 매개변수로 전달해주면 된다.
이때 person1인스턴스에서는 person1이 self이고, person2 인스턴스에서는 person2가 self가 되는 것이다.
그래서 특정 인스턴스의 속성들을 확인하고 싶을 때, print(self.변수이름) 이렇게 하면 되는데
이때의 self == 그 인스턴스 자기자신이다.
print(person1.name) # 출력: Alice
print(person1.age) # 출력: 30
print(person2.name) # 출력: Bob
print(person2.age) # 출력: 25
# 클래스 상속: super 함수
super 함수는 부모 클래스의 메서드를 호출할 때 사용된다.
상속받은 클래스에서 부모 클래스의 메서드를 확장하거나 재정의할 수 있다.
class Parent:
def __init__(self, name):
self.name = name
def display(self):
print(f"부모 클래스: {self.name}")
class Child(Parent):
def __init__(self, name, age):
super().__init__(name)
self.age = age
def display(self):
super().display()
print(f"자식 클래스: 이름={self.name}, 나이={self.age}")
child = Child("홍길동", 20)
child.display()
위 코드에서는 class Child(Parent): 에서 Child 클래스가 Parent 클래스를 상속받고 있다.
1. def __init__(self, name, age): 에서
super를 사용하여 부모 클래스의 __init__ 메서드를 호출한다.
2. def display(self): 여기서
display 메서드를 재정의하여 부모 클래스의 display 메서드를 호출한 뒤 추가 정보를 출력한다.
주어진 데이터를 특정한 연산(해시 함수)으로 변환해서 그 결과를 데이터가 저장될 인덱스로 사용한다.
이렇게 하면 좋은 점:
원소를 효율적으로 저장하고 검색할 수 있다.
예를 들어 해시 함수가 데이터를 10으로 나누어 나머지를 인덱스로 사용하는 경우,
"42"라는 데이터는 2번 인덱스에 저장된다. 이는 연산이 간단하고 빠르게 처리될 수 있기 때문에 해시 테이블은 많은 컴퓨터 과학 및 프로그래밍 문제에서 유용하게 사용된다.
정렬과 해시 테이블
해시 테이블은 본질적으로 정렬된 순서를 유지하지 않는다. 해시 테이블의 주요 목적은 데이터의 빠른 저장과 검색이다. 따라서 데이터가 삽입되는 순서나 정렬된 순서를 유지하지 않는다.
정렬된 순서가 필요할 때는 다음과 같은 방법을 사용할 수 있다:
정렬된 데이터 구조 사용: 해시 테이블 대신 이진 탐색 트리(예: AVL 트리, 레드-블랙 트리)와 같은 정렬된 데이터 구조를 사용한다. 이러한 데이터 구조는 삽입, 삭제, 검색 시에 정렬된 순서를 유지한다.
해시 테이블과 별도의 정렬된 리스트 사용: 해시 테이블에 데이터를 저장하면서, 별도의 리스트나 배열에 데이터를 삽입하여 이 리스트를 정렬된 상태로 유지한다.
데이터를 추출하여 정렬: 해시 테이블에 저장된 데이터를 한 번에 모두 추출한 후, 필요한 시점에 정렬한다.
파이썬에서 해시 테이블 역할을 하는 딕셔너리의 데이터를 정렬하는 예시:
# 해시 테이블에 데이터 삽입
hash_table = {'c': 3, 'a': 1, 'b': 2}
# 키를 기준으로 정렬
sorted_keys = sorted(hash_table.keys())
# 값(value)을 기준으로 정렬
sorted_values = sorted(hash_table.values())
print("키 기준 정렬:", sorted_keys)
print("값 기준 정렬:", sorted_values)
실행 결과:
키 기준 정렬: ['a', 'b', 'c']
값 기준 정렬: [1, 2, 3]
키 기준으로 정렬된 딕셔너리 생성하고 싶다면?
정렬된 키를 기반으로 새로운 딕셔너리를 생성하면 된다:
# 해시 테이블에 데이터 삽입
hash_table = {'c': 3, 'a': 1, 'b': 2}
# 키를 기준으로 정렬
sorted_keys = sorted(hash_table.keys())
# 정렬된 키를 기반으로 새로운 딕셔너리 생성
sorted_dict = {key: hash_table[key] for key in sorted_keys}
print("키 기준 정렬된 딕셔너리:", sorted_dict)
#결과
키 기준 정렬된 딕셔너리: {'a': 1, 'b': 2, 'c': 3}
해시 테이블은 빠른 데이터 저장과 검색을 위해 사용되며, 정렬된 순서를 유지하지 않는다. 그러나 필요에 따라 정렬된 데이터를 생성하거나, 정렬된 상태로 유지할 수 있는 방법을 사용할 수 있다.
하지만 그래서 특정 원소가 존재하는지를 검사하는 연산의 시간복잡도는 딕셔너리와 조합에서 모두 O(1)이다.
리스트나 튜플은 특정 원소가 존재하는지를 순회하면서 검색하면 O(n)만큼 걸리는 것에 비하면 엄청 빠르다.
빠르게 입력받기
import sys
sys.stdin.readline().rstrip()
리스트 안에서 문자- 숫자 자료형 변경하기
a = ['123', '456', '789']라고 할 떄,
1. 리스트 안의 문자를 숫자로 변경하는 법
a = list(map(int, a))
2. 리스트 안의 숫자를 문자로 변경하는 법
a = list(map(str, a))
<파이썬 표준 라이브러리>
1. 내장 함수(import 없이 사용 가능)
input()
print()
sum()
sum([2, 3, 4, 5, 6]) #sum 안의 이터러블 객테의 모든 원소의 합을 반환한다.
min()
max()
eval()
eval("(3+5)*7") #eval 안의 수식을 계산한 결과를 반환한다.
sorted()
sorted([9, 8, 3, 7, 2]) #이터러블 객체 오름차순으로 정렬
sorted([9, 8, 3, 7, 2], reverse = True) #이터러블 객체 내림차순으로 정렬
sorted([('길동', 35), ('순신', 75), ('아무개', 50)], key = lambda x: x[1], reverse = True) #원소를 튜플의 두번째 원소를 기준으로, 내림차순으로 정렬
#이것은
arr.sort(key = lambda x: x[1], reverse = True) 와 같다.
2. itertools
from itertools import 메서드 이름
from itertools import *
1) 순열: permutations
arr=[ 'A', 'B', 'C' ]
res = list(permutations(arr, 3)) #array에서 3개를 뽑아 나열하기
res = list(product(arr, repeat = 3)) #array에서 3개를 뽑아 중복을 허용하여 나열하기
2) 조합: combinations
arr=[ 'A', 'B', 'C']
res = list(combinations(arr, 3)) #array에서 원소 3개를 뽑기
res = list(combinations_with_replacement(arr, 3)) #array에서 원소 3개를 중복 허용해서 뽑기
3. heapq
힙으로 오름차순, 내림차순 정렬할 때의 시간복잡도: O(NlogN)
import heapq
#최소힙 구현
def heapsort_min(iterable):
h=[ ]
result =[ ]
for value in iterable:
heapq.heappush(h, value)
for i in range(len(h)):
result.append(heapq.heappop(h))
return result
#최대힙 구현
def heapsort_max(iterable):
h=[ ]
result = [ ]
for value in iterable:
heapq.heappush(h, -value)
for i in range(len(h)):
result.append(-heapq.heappop(h))
return result
4. bisect
정렬된 배열에서 특정 원소 찾을 때 유용한 이진탐색을 구현한다. bisect_left(), bisect_right()는 모두 시간복잡도 O(logN)으로 동작한다.
from bisect import bisect_left, bisect_right
arr=[1, 2, 4, 4, 8]
print(bisect_left(arr, 4)) #4를 삽입할 수 있는 가장 왼쪽 인덱스? == 2
print(bisect_right(arr, 4)) #4를 삽입할 수 있는 가장 오른쪽 인덱스? == 4
from collections import deque
queue = deque([2, 3, 4])
queue.appendleft(1) #가장 왼쪽에 1 추가
queue.append(5) #가장 오른쪽에 5 추가
print(list(queue)) #일반적인 리스트 자료형으로 변환
2) Counter
from collections import Counter
iterable 객체 내부에서 특정 원소가 몇 번 등장하는지 세는 메서드
from collections import Counter
counter['a']
6. math
수학 계산 해주는 내장함수
import math
math.factorial(x) #팩토리얼 구하기
math.sqrt(x) #제곱근 구하기
math.gcd(21, 14) #최대공약수 구하기
math.pi #파이값 출력
파이썬에서 해시 문제를 풀 때는 딕셔너리를 사용한다. 딕셔너리는 중괄호 안에 키-값 쌍을 저장하는데 이때 키를 해시하여 값을 빠르게 검색할 수 있다.
딕셔너리 사용법
1. 딕셔너리 생성
dict = { } #빈 딕셔너리 생성
2. 새로운 키-값 쌍 추가
dict["new_key"] = "new_value"
3. 값을 업데이트 하기
dict["key1"] = "updated_value"
4. 딕셔너리 값에 접근할 때는 키를 사용한다.
value = dict["key1"]
value = dict.get["key1"]
value = dict.get("key1", "default_value")
5. 딕셔너리에서 요소를 삭제하기
del dict["key1"] #키를 이용해서 삭제
value = dict.pop("key1", "default_value") #pop() 메서드로 삭제하며 값을 반환
dict.clear() #딕셔너리 안의 모든 요소 삭제
6. 딕셔너리 순회하는 법
# 키 순회
for key in my_dict:
print(key, my_dict[key])
# 값 순회
for value in my_dict.values():
print(value)
# 키-값 쌍 순회
for key, value in my_dict.items():
print(key, value)