Python 공부

[파이썬 스킬업] 2장

ra._.nie 2022. 6. 8. 15:18

(길벗, 파이썬 스킬 업)을 학습하고 개인 학습용으로 정리한 내용임.


아래에서 등장하는 다양한 기능들은 문자열, 즉 str 클래스에서 제공하는 여러 가지 기능을 사용할 수 있게 하는 연산자나 메서드임.


2.1 문자열은 불변이다

  • 파이썬의 데이터 타입
    • 가변(mutable/changeable)
      • 데이터가 즉시 변경될 수 있음. 변경하기 위해 매번 새로운 객체를 처음부터 만들 필요X.
      • list, dictionary, set
    • 불변(immutable)
      • 바뀔 수 없으므로 dictionary의 key로 사용할 수 있음. 그래서 dictionary의 key로 주로 문자열이 쓰이며, 종종 tuple이 사용되기도 함.
      • 문자열, tuple
      • 문자열의 값을 인덱스로 접근하여 일부를 수정하고자 하면 타입 에러 예외가 발생함.
my_str = '안녕'
my_str = '안녕하세요'

→ 다만 이 경우 my_str을 수정하는 것이 아니라 각 줄마다 새로운 문자열 객체가 생성되고, my_str에 재대입되기 때문에 문제 없이 잘 동작함. 서로 다른 문자열 2개가 생성되는 것이며, 이름 my_str이 재사용된 것뿐.

cf) 파이썬의 변수는 그저 객체를 호출하는 이름일 뿐이며, 같은 변수를 계속해서 재사용할 수 있음.

 

2.2 바이너리를 포함한 숫자 변환

  • int()
    • int 변환은 다른 타입 변환과는 달리 선택 사항으로 두 번째 인수를 넣을 수 있음. 해당 인수는 문자열을 숫자로 변환할 때 변환 대상 문자열의 진수를 명시함. 선택사항이며, default는 10진수를 뜻하는 10임.
n = int('10001', 2)    # 이진수 변환 # 17
n1 = int('775', 8) # 509
n2 = int('1E', 16) # 30
print('8진수 775와 16진수 1E의 값:', n1, n2)

cf) str 객체의 경우 len()을 사용하여 문자열의 길이를 알 수 있지만 float나 int 타입의 객체에선 사용 불가능(굳이 숫자의 '길이'를 알 필요는 없으니.).

  • str()
    • 숫자를 문자열 타입으로 변환하는 것은 아스키코드(ASCII)나 유니코드(unicode) 숫자로 변환하는 것과는 다름. 이는 ord 함수를 사용하여 한 번에 한 문자씩 처리해야 하는 다른 기능임.
print(ord('0')) # 48

 

2.3 문자열 연산자(+, =, *, >, 기타)

  • 문자열 메서드
    • 파이썬의 문자열 연산자를 사용하여 비교하는 모든 기능은 대, 소문자를 구분(case-sensitive)함. 
    • upper(), lower()
      • 각각 문자열 피연산자들을 모두 대문자로 만들거나 모두 소문자로 만들어줌.
      • 아스키 문자만으로 구성된 문자열일 경우 잘 동작.
    • casefold()
      • 광범위한 유니코드 문자로 이루어진 문자열 사용하고 싶다면 사용.
      • 해당 메서드를 사용하여 대, 소문자를 구분하지 않고 값을 비교. 다음과 같이 사용.
if str1.casefold() == str2.casefold():
	pass
  • 문자열 연산자
    • 문자열1 in 문자열2
      • 문자열1 전체가 문자열2에 포함되면 True 반환.
    • 문자열1 not in 문자열2
      • 문자열1 전체가 문자열2에 포함되지 않으면 True 반환.
    • 문자열 is 객체
      • 문자열과 객체가 메모리상에 동일 객체를 참조하고 있으면 True 반환. 간혹 None이나 알지 못하는 객체 타입과 비교할 때 사용. 동일한 값을 지닌 서로 다른 문자열 객체에 대해서 '=='을 사용하여 값이 같은지 테스트하면 항상 True를 반환할 테지만 is나 is not을 사용해보면 다를 수 있음.
    • 문자열 is not 객체
      • 문자열과 객체가 메모리상에 동일 객체를 참조하고 있지 않으면 True 반환.

2.4 인덱싱과 슬라이싱

  • 문자열의 값을 추출하는 두 가지 방법
    • 인덱싱(indexing)
      • 문자열 안의 특정 위치에 있는 숫자(인덱스, 색인)로 각 문자 참조.
    • 슬라이싱(slicing)
      • 파이썬의 고유 기능. 원하는 부분의 문자들을 다양한 방법으로 참조할 수 있음.
      • 슬라이싱 문법
        • string[beg: end] : beg부터 end 전까지 모든 문자 반환.
        • string[:end] : 처음부터 end 전까지 모든 문자 반환.
        • string[beg:] : beg부터 끝까지 모든 문자 반환.
        • string[:] : 문자열 전체 반환. 전체 문자열을 복사하는 연산.
        • string[beg: end: step] : beg부터 end 전까지 각 항목 간 step 간격의 문자 반환.
          • step 인수가 2면 매번 한 항목을 건너뜀.
          • step 인수가 음수면 문자열을 역순으로 순회하게 만드는 것.
        • 끝에서 마지막 N번째 문자까지 확보하고 싶다면 string[-N:]을 사용.
    • list와 비슷한 기능을 제공하는 문자열의 indexing과 slicing.
    • BUT 리스트에서는 해당 기능들로 리스트의 값들에 접근해 실제 값을 변경하는 것이 가능했지만 문자열은 불변 타입이기 때문에 indexing, slicing 혹은 값을 변경하는 그 어떤 연산자도 실제 값을 변경하지 않음.
    • 문자열의 한 인덱스를 접근하여 한 문자만 얻더라도 해당 문자의 타입은 여전히 문자열 <class 'str'> 타입. 이처럼 파이썬은 별도의 '문자' 타입 제공X.
    • 파이썬은 슬라이싱을 수행할 때 인덱스 범위를 벗어나더라도 예외를 발생시키지 않음. 대신 가능한 한 많은 부분을 반환함. 어떤 경우에는 아무 문자열도 반환하지 않을 수도 있음.

2.5 단일-문자 함수(문자 코드)

  • ord(단일문자)
    •  숫자 코드 반환.
    • 더 긴 문자 들어가면 TypeError 예외 발생.
  • chr(숫자)
    • 아스키코드나 유니코드를 한 글자 문자로 변환.
    • ord()와 정바대로 동작.
  • in 연산자
    • 모든 문자열이 빈 문자열('')을 포함하고 있다고 생각하는 반면, 모든 리스트가 빈 리스트를 항목으로 가지고 있다고 생각하지는 않는 연산자.
print('' in 'cat')        # True 출력
print([] in [1, 2, 3])    # False 출력
  • for 루프와 순회
    • 리스트를 순회하면 각 항목을 반환하는 반면, 문자열을 순회하면 문자열에 포함된 각각의 문자를 반환.
    • 이때, 리스트가 반환하는 각 항목은 list 객체가 아니라 해당 데이터의 타입이지만, 문자열 순회 시 문자열이 반환하는 건 한 문자로 구성되어 있다 할지라도 길이 1인 문자열이므로 str 타입을 반환하는 것.
s = 'Cat'
for ch in s:
    print(ch, ', type:', type(ch)) # <class 'str'>
    
d = [1,2,3,4]
for i in d:
    print(i, ', type:', type(i)) # <class 'int'>

 

2.6 'join'을 사용하여 만든 문자열

  • 불변의 성질을 거스르지 않고 새로운 문자열 조립하거나 생성하는 방법
    • 매번 기존 문자열 객체를 재사용하면서 새로운 문자열을 대입. 다만 파이썬 내부에서 메모리에 완전히 새로운 문자열을 계속 반복해서 생성하게 되기 때문에 비효율적. 한 번만 사용하고 파이썬의 '가비지 컬렉션(garbage collection)'에 의해 버려질 수천 개의 문자열을 만들어야 하기 때문에 비효율적. 빈번하게 실행된 가비지 컬렉션은 실행 시간의 비효율을 초래함.

cf) 가비지 컬렉션 : 메모리상에 더 이상 사용하지 않는 객체들을 제거하여 개발자가 직접 객체를 위한 메모리를 할당하거나 해제하는 행위를 하지 않게 도와주는 도구

# 정상적으로 작동
a_str = 'Big '
a_str = a_str + 'Bad ' # a_str += 'Bad '
a_str = a_str + 'John' # a_str += 'John'
print(a_str)

# 정상적으로 작동
a_str = 'mom'
print(a_str)

# TypeError 발생
a_str[1] = 'a'
print(a_str)
  • join 메서드
    • 구분자_문자열.join(리스트)
    • 리스트의 모든 문자열을 연결하여 하나의 커다란 문자열을 만듦.
n = ord('A')
a_lst = [ ]
for i in range(n, n + 26):
    a_lst.append(chr(i))
print(a_lst)
s = ''.join(a_lst) # ABCDEFGHIJKLMNOPQRSTUVWXYZ
s = '_'.join(a_lst) # A_B_C_D_E_F_G_H_I_J_K_L_M_N_O_P_Q_R_S_T_U_V_W_X_Y_Z

 

2.7 주요 문자열 함수(메서드)

  • input(프롬프트에 출력할 문자열)
  • len(문자열)
    • 문자열뿐만 아니라 모든 표준 컬렉션 클래스의 항목 개수를 반환
  • max(문자열)
    • 가장 높은 코드 값을 가진 문자 반환
  • min(문자열)
    • 가장 낮은 코드 값을 가진 문자 반환
  • reversed(문자열)
    • 역순 문자열을 지닌 이터레이터 반환
  • sorted(문자열)
    • 정렬된 문자열을 지닌 리스트 반환
  • reversed()와 sorted() 모두 join 메서드를 사용하면 객체를 문자열로 변환한 값을 확인할 수 있음.(그렇다면 reversed()가 반환한 이터레이터가 list처럼 취급된다는 건가? 이터레이터를 list의 한 종류로서 보는건가? join은 list의 요소들을 구분자로 구분지어 연결해주는 메서드니까.)
a_str = ''.join(reversed('Wow,Bob,wow!'))
print(a_str) # !wow,boB,woW
b_str = ''.join(sorted('Wow,Bob,wow!'))
print(b_str) # !,,BWbooowww

 

2.8 이진수와 10진수, 16진수 변환 함수

  • bin(숫자) : 숫자의 2진수 반환
  • hex(숫자) : 숫자의 16진수 반환
  • oct(숫자) : 숫자의 8진수 반환
  • 숫자 그 자체를 반환하는 것이 아니라 숫자의 모습을 한 문자열의 형태로 반환함. 그러므로 반환된 타입은 <class 'str'>

2.9 간단한 불리언('is') 메서드

  • 함수가 is로 시작하는 모든 메서드True나 False를 반환
  • 다음의 메서드는 모든 문자열이 해당 조건을 만족해야 True가 반환됨
  • 대, 소문자의 유무 등을 테스트
  • str.isalnum() : 모든 문자가 글자와 숫자로 이루어졌으며, 최소한 문자가 하나 이상 있는 경우
  • str.isalpha() : 모든 문자가 알파벳 글자. 최소한 문자가 하나 이상.
  • str.isdecimal() : 모든 문자가 10진수 숫자. 최소한 문자가 하나 이상. isdigit()과 비슷하지만 유니코드 문자와 사용.
  • str.isdigit() : 모든 문자가 10진수 숫자. 최소한 문자가 하나 이상.
  • str.isidentifier() : 문자열이 유효한 파이썬 식별자 이름 규칙을 지키고 있는 경우.
  • str.islower() : 모든 문자 소문자. 참고로 알파벳이 아닌 문자가 포함될 수도 있음.
  • str.isprintable() : 모든 문자가 출력 가능한 문자인 경우. \n과 \t는 제외.
  • str.isspace() : 모든 문자가 공백 문자이며, 최소한 문자가 하나 이상.
  • str.istitle() : 모든 문자가 유효한 제목이며, 최소한 문자가 하나 이상. 첫 문자만 대문자고 나머지는 모두 소문자면 조건 만족. 문자 사이에 공백 문자나 구분 문자 있을 수 있음.
  • str.isupper(): 모든 문자가 대문자. 참고로 알파벳이 아닌 문자가 포함될 수 있음.

cf) 공백 문자 =  ‘빈칸’, ‘탭’, ‘개행 문자’와 같이 눈에 보이지 않지만 ‘빈칸’으로 이루어진 문자들

 

2.10 대, 소문자 변환 메서드

  • 문자열.lower() : 모두 소문자인 문자열 생성
  • 문자열.upper() : 모두 대문자인 문자열 생성
  • 문자열.title() : '제목'의 조건에 적합하게 생성
  • 문자열.swapcase() : 대소문자를 서로 변경
  • 변환하고 나면 신규 문자열을 반환함. 기존 문자열 데이터는 불변이며, 기존 값이 변경되지는 않음.

2.11 검색-교체 메서드

  • 문자열.startwith(부분문자열) : 접두사 찾으면 True 반환
  • 문자열.endswith(부분문자열) : 접미사 찾으면 True 반환
    • 빈 문자열이어도 위 두 메서드 둘 다 에러 없이 동작. 인수인 부분 문자열이 빈 문자열이라면 반환값은 항상 True.
  • 문자열.count(부분문자열 [, 시작 [, 종료]]) : 인수로 넣은 부분 문자열이 등장하는 횟수 반환. 검색할 시작 위치와 끝 위치를 선택 사항으로 입력할 수 있음.
  • 문자열.find(부분문자열 [, 시작 [, 종료]]) : '부분문자열'이 발견되는 첫 번째 위치를 양수 인덱스로 반환. -1 반환하면 찾지 못했다는 의미. 부분문자열이 나타나는 모든 위치를 확보하고 싶다면 다음과 같이 루프 안에서 find 메서드를 호출하면 됨.
frank_str = 'doo be doo be doo...'
n = -1

while True:
    n = frank_str.find('doo', n + 1)
    if n == -1:
      break
    print(n, end=' ')
  • 문자열.index() : find 메서드와 거의 똑같지만 find()와는 달리 '부분문자열'을 찾지 못하면 -1을 반환하지 않음. 대신 ValueError 예외를 발생시킴.
  • 문자열.rfind() : find()와 같지만, 끝에서부터 검색. 부분문자열을 역순으로 변경한다는 것이 아니라 '부분문자열'이 등장하는 '마지막 단어'의 '첫 글자 인덱스 위치'를 반환함.
  • 문자열.replace(기존, 신규 [, 제한횟수]) : 기존 값을 신규 값으로 '모두' 변경. 당연히 기존 문자열 값을 '변경'할 수 없기 때문에 새로운 문자열이 생성됨.

2.12 'split'을 활용한 입력 값 쪼개기

  • 토큰화(tokenizing)
    • 입력받은 문장을 각 단어, 구, 숫자로 분리.
    • split 메서드
      • 입력_문자열.split(구분_문자열=None)
      • '입력_문자열'의 부분 문자열로 이루어진 리스트 반환.
      • '구분_문자열'은 단어를 쪼개는 기준인 구분자로서 동작.
      • '구분_문자열'이 None이면 공백문자(빈칸, 탭, 개행 문자)를 기준으로 토큰을 구분함. 이 때 None으로 설정되면 공백문자의 개수는 상관없음. 다만 구분자로서 직접 공백문자의 개수를 지정해준다면 그 개수가 충실하게 지켜져서 해당 구분자에 의해 정확하게 구분되어져야 함.

2.13 앞뒤 문자 제거하기

  • 앞뒤 문자 제거하기(stripping) 기능
    • 문자열.strip(제거_문자열=' ') : 앞뒤 문자 지우기
    • 문자열.lstrip(제거_문자열=' ') : 앞 문자 지우기
    • 문자열.rstrip(제거_문자열=' ') : 뒤 문자 지우기

2.14 자리 맞춤 메서드

  • 텍스트 자리 맞춤(justification)을 위한 기초적인 기법
  • formating된 문자열을 반환해줌.
  • 문자열 텍스트를 제외한 나머지는 '채우기_문자'로 채워짐. 
    • 문자열.ljust(길이 [, 채우기_문자]) : 왼쪽 자리 맞춤
    • 문자열.rjust(길이 [, 채우기_문자]) : 오른쪽 자리 맞춤
    • 문자열.center(길이 [, 채우기_문자]) : 텍스트를 가운데에 위치. 완벽하게 가운데에 위치할 수 없는 경우 왼쪽에 치우치게 문자열 텍스트를 이동시킴.
    • 숫자_문자열.zfill(길이) : 숫자 0 채우기

2.17 실습 문제

1. 문자열을 프롬프트에서 입력하여 모음과 자음 숫자를 세어서 출력하는 프로그램을 작성하라.( (힌트) 코드를 최소화하기 위해 in not in 연산자를 사용한다.)

s = input('문자열 입력>> ')

consonants, vowels = 0, 0
for i in range(len(s)):
    if s[i].lower() in 'aeiou':
        vowels += 1
    else:
        consonants += 1
print('자음 수: ', consonants, ' 모음 수: ', vowels)

2. 문자열의 앞 두 글자와 끝 두 글자를 효율적으로 제거하는 함수를 작성하라. 반환값은 빈 문자열이다.

# 문제 제대로 이해 못 함.
# 아직 코드 완성 X
def eliminating(s):
    for _ in range(2):
        s.strip()
    return s

s = input('문자열 입력>> ')
eliminating(s)
print(s)