Python 스터디 하면서 했던 내용들을 복습하면서 어려웠던 것들을 정리해보려 합니다.
x = float("inf")
x = float("-inf")
float 에서 소수점 연산의 경우 항상 정확하진 않다는 것을 알 수 있습니다.
바로 부동소수점 문제인데요.
예를들어 0.1과 0.2의 연산의 경우 당연히 0.3이 될 것이라고 생각했지만 0.30000000000000004가 출력이됩니다. 2진수로만 연산하는 컴퓨터의 특성상 0.1과 0.2를 2진수로 변환한후에 연산을 합니다.
print(0.1 + 0.2)
# 출력: 0.30000000000000004
.1 * 2 == .2 ---- 0
.2 * 2 == .4 ---- 0
.4 * 2 == .8 ---- 0
.8 * 2 == 1.6 ---- 0
.6 * 2 == 1.2 ---- 1
.2 * 2 == .4 ---- 1 => 여기서부터는 계속 반복됩니다.
.4 * 2 == .8 ---- 0
.8 * 2 == 1.6 ---- 0
.6 * 2 == 1.2 ---- 1
.2 * 2 == .4 ---- 1
0.1을 2진수로 연산하면 0.000110011...의 무한소수의 형태를 보입니다.
.2 * 2 == .4 ---- 0
.4 * 2 == .8 ---- 0
.8 * 2 == 1.6 ---- 0
.6 * 2 == 1.2 ---- 1
.2 * 2 == .4 ---- 1 => 여기서부터는 계속 반복됩니다.
.4 * 2 == .8 ---- 0
.8 * 2 == 1.6 ---- 0
.6 * 2 == 1.2 ---- 1
.2 * 2 == .4 ---- 1
0.2을 2진수로 연산하면 0.00110011...의 무한소수의 형태를 보입니다.
즉, 무한소수끼리의 연산으로 0.3의 값과 가깝지만 정확하게 연산이 되지 않는다는 것을 알 수 있습니다.
이를 해결하는 방법은 여러가지가 있지만 여기서 정리할 것은 두가지 입니다.
import decimal
print(float(decimal.Decimal('0.1') + decimal.Decimal('0.2')))
# 출력: 0.3
print((0.1*10 + 0.2*10)/10)
# 출력: 0.3
슬라이싱은 [시작지점:원하는 정지시점+1:간격]
의 형태로 사용한다. 여기서 간격의 기본값은 1이고, 원하는 정지시점 +1을 한 이유는 원하는 정지시점 부분은 포함을 안하기 때문입니다.
예를 들어서 [1:10:1]
일 경우에 정지시점에 10은 포함이 되지 않는다. 즉 1부터 9까지라는 말입니다.
s = '문자열 슬라이싱을 연습해보겠습니다.'
print(s[4:])
print(s[:])
print(s[::-1]) # 간격에 -1을 입력할 경우 역순
print(s[::2]) # 간격에 2를 입력할 경우 2칸씩
# 출력 : 슬라이싱을 연습해보겠습니다.
# 출력 : 문자열 슬라이싱을 연습해보겠습니다.
# 출력 : .다니습겠보해습연 을싱이라슬 열자문
# 출력 : 문열슬이을연해겠니.
그리고 슬라이싱은 지정한 범위가 넘어가더라도 에러가 발생하지 않습니다.
s = '이렇게 지정한 범위가 넘어가도 에러가 안 생겨요'
print(s[5:30])
# 출력 : 정한 범위가 넘어가도 에러가 안 생겨요
s = "find와 index의 공통점과 차이점을 알아봅시다."
print(s.find("알아봅시다."))
print(s.index("알아봅시다."))
# 출력 : 23
# 출력 : 23
print(s.find("이것은 없는 문자열입니다."))
print(s.index("이것은 없는 문자열입니다."))
# 출력 : -1
# 출력 : ValueError: substring not found
s = "Let's talk about count."
print(s.count("t"))
# 출력 : 4 # t가 4개가 들어있다.
s1 = ' strip이 무엇인지 알아봅시다. '
print(s1.strip())
# 출력 : strip이 무엇인지 알아봅시다.
s2 = ' ,!!. strip이 무엇인지 알아봅시다. ,!!. '
print(s1.strip(' ,!.'))
# 출력 : strip이 무엇인지 알아봅시다
s = 'replace에 대해서 알아봅시다.'
print(s.replace('대해서', '무슨 기능이 있을지'))
# 출력 : replace에 무슨 기능이 있을지 알아봅시다.
# 주의!!
# s 자체는 바뀌지 않습니다.
print(s)
# 출력 : replace에 대해서 알아봅시다.
l = ['join에', '대해서', '알아봅시다.']
print('_'.join(l))
# 출력 : join에_대해서_알아봅시다.
s1 = '1q2w3e4r'
s2 = '1!2@3#4$'
s3 = '1234'
print(s1.isdigit())
print(s2.isdigit())
print(s3.isdigit())
# 출력 : False
# 출력 : False
# 출력 : True
s = '문자열입니다.'
print(s.rjust(14, '_'))
# 출력 : _______문자열입니다.
s = '문자열입니다.'
print(s.ljust(14, '_'))
# 출력 : 문자열입니다._______
s = '문자열입니다.'
print(s.center(15, '_'))
# 출력 : ____문자열입니다.____
s = "27"
use_zfill = s.zfill(5)
print(use_zfill)
# 출력 : 00027
maketrans
를 사용하여 변환 테이블 생성
table = str.maketrans('rat', 'cat')
translate
메서드로 문자열 치환
s = 'translate에 대해서 알아봅시다.'
use_translate = s.translate(table)
print(use_translate)
# 출력 ; tcanslate에 대해서 알아봅시다.
maketrans
를 사용하여 변환 테이블 생성
table = str.maketrans('', '', 'rat')
translate
메서드로 문자열 치환
s = 'translate에 대해서 알아봅시다.'
use_translate = s.translate(table)
print(use_translate)
# 출력 ; nsle에 대해서 알아봅시다.
l1 = ['리', '스', '트', '의']
l2 = ['덧', '셈']
t1 = ('튜', '플', '의')
t2 = ('덧', '셈')
print(l1 + l2)
print(t1 + t2)
# 출력 : ['리', '스', '트', '의', '덧', '셈']
# 출력 : ('튜', '플', '의', '덧', '셈')
print({1, 2, 3, 4, 5, 6, 7, 8, 9} - {1, 3, 5, 7, 9})
# 출력 : {8, 2, 4, 6} -> 요소의 순서는 랜덤입니다!
/
과//
의 차이) -> 개인적으로 자꾸 헷갈리는 부분/
연산자는 항상 float 형식으로 반환이 됩니다.//
연산자는 항상 int 형식으로 반환이 됩니다.//
의 경우는 내림인데요, 음수에서의 내림과 양수에서의 내림이 헷갈릴 수 있습니다.print(10 // 3)
# 출력 : 3 -> 3.333333....에서 내림하여 3으로 출력
print(-10 // 3)
# 출력 : -4 -> -3.333333....에서 내림하여 -4으로 출력
print(-3 ** 2)
# 9가 출력될 것 같지만 -9가 출력된다.
# 왜? -부호가 연산 후에 붙기 때문
A and B
연산에서 A가 False면 B는 평가되지 않는다. 바로 False로 결정됨.A or B
연산에서 A가 True면 B는 평가되지 않는다. 바로 True로 결정됨.논리 연산자에서 우선순위는 not / and / or 순으로 되어있다.
l = [1, 2, 3, 4, 5]
l[3] = 5000
print(l) # 출력 : [1, 2, 3, 5000, 5]
s = 'cantchange'
s[3] = 'p'
print(s) # TypeError: 'str' object does not support item assignment
l = [[1, 2, 3], [4, 5], 6, 7, 8]
print(l[0][2]) # 출력 : 3
la = [1, 2, 3, 4, 5]
lb = [6, 7, 8, 9, 10]
print(la + lb) # 출력 : [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
l = [1, 2, 3, 4, 5]
print(l * 2) # 출력 : [1, 2, 3, 4, 5, 1, 2, 3, 4, 5]
l = [1, 2, 3, 4, 5]
print(l[3:20]) # 출력 : [4, 5]
a = [3, 4, 5]
l = [1, 2, 'sss', a, 5]
print(id(1), id(l[0])) # 출력 : 2180830789872 2180830789872
print(id('sss'), id(l[2])) # 출력 : 2180835911152 2180835911152
print(id(a), id(l[3])) # 출력 : 2180835991616 2180835991616
print(id(a[2]), id(l[4])) # 출력 : 2180830790000 2180830790000
동일한 주소값(id)를 출력하는 모습을 볼 수 있습니다.
리스트 메모리 구조의 장점은 다양한 자료형을 원소로 포함할 수 있고, 데이터의 추가 및 삭제가 상대적으로 효율적입니다.
하지만 참조를 저장하기 위한 추가 메모리 공간이 필요하고, 데이터가 연속적인 메모리 공간에 저장되지 않아 캐시의 지역성이 떨어진다는 단점이 있습니다.
리스트의 메모리 구조는 다양한 자료형과 동적인 크기 변경을 지원하기 위해 트레이드 오프로 일부 메모리 및 성능 효율성을 포기하고 있습니다.
CPU와 가장 가까운 캐시를 캐시 메모리라 합니다.
이때 캐시 메모리가 역할을 정상적으로 수행하기 위해서 CPU가 어떤 데이터를 원할 것인지를 예측할 수 있어야 합니다.
CPU가 원하는 데이터가 캐시에 있을 확률을 뜻하는 Hit rate를 극대화 시키기위해 데이터 지역성을 사용하는데 이 지역성에는 대표적으로 시간적 지역성과 공간적 지역성 2가지가 있습니다.
그 중 여기서 의미하는 지역성은 공간적 지역성입니다. 이는 기억장치 내에 인접하여 저장되어있는 데이터들이 연속적으로 접근될 가능성이 높아지는 것을 의미합니다.
어떤 것을 얻기 위해 다른 것을 포기하는 것.
l = [1, 2, 3, 4, 5]
l.append(100)
print(l) # 출력 : [1, 2, 3, 4, 5, 100]
l = [1, 2, 3, 4, 5]
newl = l.copy()
newl[0] = 2727
print(l) # 출력 : [1, 2, 3, 4, 5]
print(newl) # 출력 : [2727, 2, 3, 4, 5]
여기서는 newl
이 [1, 2, 3, 4, 5]
를 가리키는 것이 아닌 같은 주소를 가진 요소를 갖고 있는 리스트를 복사하는 것입니다.
copy()를 하지 않을 경우
l = [1, 2, 3, 4, 5]
newl = l
newl[0] = 2727
print(l) # 출력 : [2727, 2, 3, 4, 5]
print(newl) # 출력 : [2727, 2, 3, 4, 5]
여기서는 newl
이 [1, 2, 3, 4, 5]
를 가리키기 때문에 newl[0]
이 l[0]
과 동일한 요소입니다.
깊은복사
copy 모듈을 사용하면 깊은 복사를 할 수 있습니다.
import copy
l1 = [10000]
l2 = [27, l1]
l3 = [87, l1]
l4 = [l2, l3]
l5 = copy.deepcopy(l4)
l1[0] = 999
print(l4) # 출력 : [[27, [999]], [87, [999]]]
print(l5) # 출력 : [[27, [10000]], [87, [10000]]] # 깊은 복사
d = dict([('name', '이름의 value입니다.'),('age', '나이의 value입니다.')])
print(d)
# 출력 : {'name': '이름의 value입니다.', 'age': '나이의 value입니다.'}
d = dict(name = 'licat', age = 10)
print(d)
# 출력 : {'name': 'licat', 'age': 10}
clear()
딕셔너리의 모든 key-value 쌍을 삭제합니다.
fromkeys()
시퀀스 자료형으로 딕셔너리를 생성할 수 있습니다.
keys = ['요소1', '요소2', '요소3']
value = None
print(dict.fromkeys(keys, value))
# 출력 : {'요소1': None, '요소2': None, '요소3': None}
시퀀스 자료형으로 하나하나 요소를 value로 지정하는 것은 불가능합니다.
keys = ['요소1', '요소2', '요소3']
values = ['값1', '값2', '값3']
print(dict.fromkeys(keys, values))
# 출력 : {'요소1': ['값1', '값2', '값3'], '요소2': ['값1', '값2', '값3'], '요소3': ['값1', '값2', '값3']}
items()
딕셔너리의 키와 값을 쌍으로 추출할 때 사용합니다.
d = {'요소1': ['값1', '값2', '값3'], '요소2': ['값1', '값2', '값3'], '요소3': ['값1', '값2', '값3']}
print(d.items())
# 출력 : dict_items([('요소1', ['값1', '값2', '값3']), ('요소2', ['값1', '값2', '값3']), ('요소3', ['값1', '값2', '값3'])])
# 리스트로 변환시
print(list(d.items()))
# 출력 : [('요소1', ['값1', '값2', '값3']), ('요소2', ['값1', '값2', '값3']), ('요소3', ['값1', '값2', '값3'])]
pop()
주어진 key의 value를 반환하고 key-value 쌍을 삭제합니다.
d = {'요소1': '값1', '요소2': '값2', '요소3': '값3'}
popd = d.pop('요소1')
print(popd)
print(d)
# 출력 : 값1
# 출력 : {'요소2': '값2', '요소3': '값3'}
popitem()
마지막 key-value 쌍을 반환하고 삭제합니다.
d = {'요소1': '값1', '요소2': '값2', '요소3': '값3'}
poplast = d.popitem()
print(poplast)
print(d)
# 출력 : {'요소1': '값1'}
# 출력 : {'요소2': '값2', '요소3': '값3'}
setdefault()
주어진 key의 value를 반환합니다. key가 없을 경우 새로 지정합니다.
key가 있을 경우 value가 수정되지 않습니다.
d = {'요소1': '값1', '요소2': '값2', '요소3': '값3'}
dd = d.setdefault({'요소4' : '값4'}
print(dd)
print(d)
# 출력 : 값4
# 출력 : {'요소1': '값1', '요소2': '값2', '요소3': '값3', '요소4': '값4'}
update()
딕셔너리의 새로운 값을 추가할 때 사용합니다.
d = {'요소1': '값1', '요소2': '값2', '요소3': '값3'}
d.update({'요소4' : '값4'}
print(d)
# 출력 : {'요소1': '값1', '요소2': '값2', '요소3': '값3', '요소4': '값4'}
Set 자료형은 중복을 허용하지 않으며, 순서가 없습니다.
|
)set1 = {2, 4, 6}
set2 = {3, 5, 7}
합집합 = set1 | set2
print(unionset)
# 출력 : {2, 3, 4, 5, 6, 7}
&
)set1 = {2, 4, 6}
set2 = {3, 4, 5, 6, 7}
교집합 = set1 & set2
print(교집합)
# 출력 : {4, 6}
-
)set1 = {2, 3, 4, 5, 6, 7}
set2 = {3, 5, 7}
차집합 = set1 - set2
print(차집합)
# 출력 : {2, 4, 6}
in
키워드 사용
set1 = {2, 3, 4, 5, 6, 7}
print(4 in set1)
print(9 in set1)
# 출력 : True
# 출력 : False
KeyError
가 발생합니다.set1 = {2, 3, 4, 5, 6}
set1.remove(3)
print(set1) # 출력 : {2, 4, 5, 6}
set1.remove(1) # KeyError
set1 = {2, 3, 4, 5, 6}
set1.discard(3)
print(set1) # 출력 : {2, 4, 5, 6}
set1.discard(1) # 에러가 발생하지 않습니다.
set1 = {2, 3, 4, 5, 6, 7}
set2 = {3, 5, 7}
print(set1.difference(set2))
# 출력 : {2, 4, 6}
set1 = {2, 3, 4, 5, 6, 7}
set2 = {3, 5, 7}
set1.difference_update(set2)
print(set1)
# 출력 : {2, 4, 6}
set1 = {2, 3, 4, 5, 6, 7}
set2 = {3, 5, 9}
print(set1.intersection(set2))
# 출력 : {3, 5}
set1 = {2, 3, 4, 5, 6, 7}
set2 = {3, 5, 9}
set1.intersection_update(set2)
print(set1)
# 출력 : {3, 5}