Ai Tech Pre-course Lec.2

Lilmeow·2023년 8월 30일
0

Boostcamp Step.1

목록 보기
2/10
post-thumbnail

Lec2: String and Advanced Function Concept

01_String

1) String?

  • 시퀀스 자료형으로 문자형 data를 메모리에 저장
  • 영문자 한 글자는 1byte의 메모리공간을 사용
import sys # sys module을 호출
print(sys.getsizeof("a"), sys.getsizeof('ab'), sys.getsizeof('abc'))

50 51 52
#각 문자열의 메모리 사이즈 출력

  • string은 1byte 크기로 한 글자씩 메모리 공간이 할당됨
a = "abcde"

a : 0100 1001
b : 0100 1010
c : 0100 1011
d : 0100 1100
e : 0100 1101

2) 1byte의 메모리 공간이란

  • 컴퓨터는 이진수로 데이터를 저장
  • 이진수 한 자릿수는 1bit로 저장됨
  • 즉 1bit는 0 또는 1
  • 1byte = 8bit = 2^8 = 256까지 저장 가능
  • 컴퓨터는 문자를 직접적으로 인식 X >> 모든 데이터는 이진수로 인식
  • 이를 위해 이진수를 문자로 변환하는 표준 규칙을 정함
  • 이러한 규칙에 따라 문자를 이진수로 변환하여 저장하거나, 저장된 이진수를 숫자로 변환하여 표시함
  • ex. 대문자 U는 이진수로 '1000011' 변환됨 (UTF-8 기준)

3) 프로그램 언어에서 Data Type

  • 각 타입 별로 메모리 공간을 할당 받은 크기가 다름
  • 메모리 공간에 따라 표현할 수 있는 숫자범위가 다름
  • 데이터 타입은 메모리의 효율적 활용을 위해 매우 중요

4) 문자열 특징 : Indexing

  • 문자열의 각 문자는 개별 주소(offset)를 가짐
  • 이 주소를 사용해 할당된 값을 가져오는 것이 인덱싱
  • 리스트와 같은 형태로 데이터를 처리함
a = 'abcde'
print(a[0], a[4]) # a e
print(a[-1], a[-5]) # e a
  • 각 문자의 offset은 왼쪽에선 0부터, 오른쪽에선 -1부터 시작함

5) 문자열 특징 : Slicing

문자열의 주소값을 기반으로 문자열의 부분값을 반환

print(a[0:6], a[-9:]) # a 변수의 0부터 5까지, -9부터 끝까지
print(a[:]) # a 변수의 처음부터 끝까지
print(a[-50:50]) # 범위를 넘어갈 경우 자동으로 최대 범위를 지정
print(a[::2], a[::-1]) # 2칸 단위로, 역으로 슬라이싱
  • 문자열 연산 및 포함여부 검사
a = 'AAA'
b = 'BBB'
print(a + b)
print(a * 2 + b * 2)

AAABBB
AAAAAABBBBBB

if 'A' in a:
    print(True)
else:
    print(False)

True

6) 문자열 함수

len(x) : 문자열의 문자 개수를 반환
x.upper() : 대문자로 변환
x.lower() : 소문자로 변환
x.capitalize : 첫 문자를 대문자로 변환
x.title() : 제목형태로 변환, 띄워쓰기 후 첫 글자만 대문자
x.count('abc') : 문자열 a에 'abc'가 들어간 횟수 반환
x.find('abc') / x.rfind('abc') : 문자열 a에 'abc'가 들어간 위치(offset) 반환
x.startwith('abc') : 문자열 a는 'abc'로 시작하는 문자열인지 여부 반환
x.endwith('abc') : 문자열 a는 'abc'로 끝나는 문자열인지 여부 반환

x.strip() : 좌우 공백 제거
x.rstrip() : 오른쪽 공백 제거
x.lstrip() : 왼쪽 공백 제거
x.split() : 공백을 기준으로 나눠 리스트로 반환
x.split('abc') : abc를 기준으로 나눠 리스트로 반환
x.isdigit() : 문자열이 숫자인지 여부 반환
x.islower() : 문자열이 소문자인지 여부 반환
x.isupper() : 문자열이 대문자인지 여부 반환

7) 다양한 문자열 표현

  • 문자열 선언은 ''나 ""를 활용
  • 두줄 이상은 세번 연속 사용 '''''', """"""
print("""aaa
bbb
ccc""")

aaa
bbb
ccc

8) 특수 문자

\ [Enter] : 다음 줄과 연속
print("aaa\
bbb") # aaabbb
\n : 줄 바꿈
\ : \ 출력
\t : tab 출력
\' : ' 출력
\" : " 출력
\b : back space
\e : esc

9) Raw String

  • 특수 문자를 무시하고 그대로 출력
print(r"aaa\nbbb''")

aaa\nbbb\''

  • 실습용 Library, txt file Download
import wget
wget.download("https://raw.githubusercontent.com/TeamLab/introduction_to_python_TEAMLAB_MOOC/master/code/6/yesterday.txt")
  • 노래 가사에 특정 단어가 나오는 횟수 구하기
f = open("yesterday.txt", 'r')
yesterday_lyric = ""
while True:
    line = f.readline()
    if not line:
        break
    yesterday_lyric = yesterday_lyric + line.strip() + "\n"
f.close()
n_of_yesterday = yesterday_lyric.upper().count("YESTERDAY") # 대소문자 구분 제거
print ("Number of a Word 'Yesterday'" , n_of_yesterday)

Number of a Word 'Yesterday' 9

02_Function

1) Call By Object Reference : 함수 호출 방식 개요

함수에서 Parameter를 전달하는 방식

  • 값에 의한 호출(Call by Value)
    : 함수에 인자를 넘길 때 값만 넘긴다, 함수 내에 인자 값 변경 시, 호출자에게 영향을 주지 않음
  • 참조에 의한 호출(Call by Reference)
    : 함수에 인자를 넘길 떄 메모리 주소를 넘긴다, 함수 내에 인자 값 변경 시, 호출자의 값도 변경됨
  • 객체 참조에 의한 호출(Call by Object Reference)
    : 파이썬 함수의 호출 방식, 객체의 주소가 함수로 전달된다.
    전달된 객체를 참조하여 변경 시 호출자에게 영향을 주나, 새로운 객체를 만들 경우 호출자에게 영향을 주지 않음
def spam(eggs):
    eggs.append(1) # 3. 기존 객체의 주소값(ham과 eggs가 가리키는 메모리)에 [1] 추가 >> ham, eggs = [0, 1]
    eggs = [2, 3] # 4. 새로운 객체 생성 >> ham = [0, 1] / eggs = [2, 3]
ham = [0] # 1. ham이라는 리스트가 만들어지고 하나의 메모리를 가리킨다
spam(ham) # 2. spam이라는 함수에 들어가서 eggs라는 걸로 받고 ham과 같은 메모리를 가리킨다
print(ham) # [0, 1]

2) Swap

  • 함수를 통해 변수 간의 값을 교환(swap)하는 함수
  • Call by XXXX를 설명하기 위한 전통적인 함수 예시

Quiz: a = [1, 2, 3, 4, 5]일 때, 아래 함수 중 실제 Swap이 일어나는 함수?

a = [1, 2, 3, 4, 5]
def swap_value(x, y):
    temp = x
    x = y
    y = temp
swap_value(a[1], a[2]) # 주소만 바뀌기 때문에 a라는 리스트 자체는 영향이 없다
print(a) # [1,2,3,4,5]
def swap_offset(offset_x, offset_y):
    temp = a[offset_x]
    a[offset_x] = a[offset_y]
    a[offset_y] = temp
swap_offset(1, 2) # 리스트 인덱스 자체를 건드리기 때문에 값이 바뀐다
print(a) # [1,3,2,4,5]
def swap_reference(list, offset_x, offset_y):
    temp = list[offset_x]
    list[offset_x] = list[offset_y]
    list[offset_y] = temp
swap_reference(a, 1, 2) # 밖에 있는 객체(리스트)를 유지하면서 값을 바꿔준다
print(a) # [1,3,2,4,5]
  • swap_offset : a 리스트의 전역 변수 값을 직접 변경
  • swap_reference : a 리스트 객체의 주소 값을 받아 값을 변경

3) Function - Scoping Rule

변수의 범위(Scoping Rule)

  • 변수가 사용되는 범위(함수 또는 메인 프로그램)
  • 지역변수(local variable) : 함수 내에서만 사용
  • 전역변수(Global variable) : 프로그램 전체에서 사용
def test(t):
    print(x) # 함수 밖의 x
    t = 20 # 함수 내의 t, 새롭게 20이라는 값을 할당했으니 x라는 변수와는 관계가 없어진다
    print ("In Function :", t)
x = 10
test(x)
print(t) # t는 지역변수이므로 밖에서는 존재하는 변수 취급 : NameError, not defined

10
In Function : 20

def test(t):
    t = 20
    print ("In Function :", t)
x = 10
print ("Before :", x) # 10
test(x) # 함수 호출
print ("After :", x) # 10 – 함수 내부의 t는 새로운 주소값을 가짐

Before : 10
In Function : 20
After : 10

  • 전역변수는 함수에서 사용 가능
  • 하지만 함수 내에 전역 변수와 같은 이름의 변수를 선언하면 새로운 지역 변수가 생김
def f():
    s = "I love London!" # 지역변수 s를 바꾸는 것, 밖의 전역변수 s는 값의 변화가 없다
    print(s)
s = "I love Paris!"
f()
print(s)

I love London!
I love Paris!

  • 함수 내에서 전역변수 사용 시 global 키워드 사용
def f():
    global s
    s = "I love London!"
    print(s)
s = "I love Paris!"
f()
print(s)

I love London!
I love London!

def calculate(x, y):
    total = x + y # 새로운 값이 할당되어 함수 내 total은 지역변수가 됨
    print ("In Function")
    print ("a:", str(a), "b:", str(b), "a+b:", str(a+b), "total :", str(total))
    return total

a = 5 # a와 b는 전역변수
b = 7
total = 0 # 전역변수 total
print ("In Program - 1")
print ("a:", str(a), "b:", str(b), "a+b:", str(a+b))

sum = calculate (a,b)
print ("After Calculation")
print ("Total :", str(total), " Sum:", str(sum)) # 지역변수는 전역변수에 영향 X

In Program - 1
a: 5 b: 7 a+b: 12
After Calculation
Total : 0 Sum: 12

4) Recursive Function : 재귀함수

  • 자기자신을 호출하는 함수
  • 점화식과 같은 재귀적 수학 모형을 표현할 때 사용
  • 재귀 종료 조건 존재, 종료 조건까지 함수 호출 반복
def factorial(n):
    if n == 1:
        return 1
    else:
        return n * factorial(n - 1)
print(factorial(int(input("Input Number for Factorial Calculation: "))))

5) Function Type Hints

  • 파이썬의 가장 큰 특징 : Dynamic Typing
    처음 함수를 사용하는 사용자가 Interface를 알기 어렵다는 단점이 있다
  • 파이썬 3.5버전 이후로는 PEP 484에 기반하여 type hints 기능 제공

Type Hints의 장점

  • 사용자에게 인터페이스를 명확히 알려줄 수 있다
  • 함수의 문서화 시, parameter에 대한 정보를 명확히 알 수 있다
  • mypy 또는 IDE, linter 등을 통해 코드의 발생 가능한 오류를 사전에 확인
  • 시스템 전체적인 안정성을 확보할 수 있다

6) Function Docstring

  • 파이썬 함수에 대한 상세스펙을 사전에 작성 >> 함수 사용자의 이행도 증가
  • 세개의 따옴표로 Docstring 영역 표시(함수명 아래)

7) PEP8 - Python Coding Convention의 기준

  • 최근에는 black module을 활용하여 pep8 like 수준을 준수

  • black codename.py 명령을 사용

  • 들여쓰기 Tab VS 4Space > 4Space 권장

  • 명확한 규칙은 없다, 팀마다, 프로젝트마다 유동적으로, 중요한 건 일관성

  • 한 줄은 최대 79자까지

  • 불필요한 공백은 피하기

  • '='연산자는 한칸 이상 안 띄운다

  • 주석은 항상 갱신, 불필요한 주석 삭제

  • 코드의 마지막에는 항상 한 줄 추가

  • 소문자 l, 대문자 o, 대문자 i 최소한으로 사용 > lIO0

  • 함수명은 소문자로 구성, 필요시 밑줄로 구분

  • flake8 module로 체크 > flake8 <파일명>

  • conda install -c anaconda flake8

8) 함수 작성 가이드 라인

  • 함수는 가능하면 짧게 작성할 것 (줄 수를 줄일 것)
  • 함수 이름에 함수의 역할, 의도가 명확히 들어낼 것 : VO형태(동사목적어)
def print_hello_world():
    print("Hello, World")
def get_hello_world():
    return "Hello, World"
  • 하나의 함수에는 유사한 역할을 하는 코드만 포함
def add_variables(x,y):
    return x + y
def add_variables(x,y): # 더하는 함수인데 print 기능을 포함
    print (x, y)
    return x + y
  • 인자로 받은 값 자체를 바꾸진 말 것(임시변수 선언)
# before
def count_word(string_variable):
    string_variable = list(string_variable) # 원래 호출된 값에 변화가 생길 수 있으니, 복사된 값을 사용
    return len(string_variable)
# after
def count_word(string_variable):
    return len(string_variable)

9) 함수는 언제 만드는가?

  • 공통적으로 사용되는 코드는 함수로 변환
  • 복잡한 수식 > 식별 가능한 이름의 함수로 변환
  • 복잡한 조건 > 식별 가능한 이름의 함수로 변환
# before
a = 5
if (a > 3):
    print "Hello World"
    print "Hello Gachon"
if (a > 4):
    print "Hello World"
    print "Hello Gachon"
if (a > 5):
    print "Hello World"
    print "Hello Gachon"
# after
def print_hello():
    print "Hello World"
    print "Hello Gachon"
a = 5
if (a > 3):
    print_hello()
if (a > 4):
    print_hello()
if (a > 5):
    print_hello()
# before
import math
a = 1; b = -2; c = 1

print ((-b + math.sqrt(b ** 2 - (4 * a * c)) ) / (2 * a))
print ((-b - math.sqrt(b ** 2 - (4 * a * c)) ) / (2 * a))

# after
import math

def get_result_quadratic_equation(a, b, c):
    values = []
    values.append((-b + math.sqrt(b ** 2 - (4 * a * c)) ) / (2 * a))
    values.append((-b - math.sqrt(b ** 2 - (4 * a * c)) ) / (2 * a))
    return values

print (get_result_quadratic_equation(1,-2,1))

0개의 댓글