FreeCodingCamp

Gi Woon Lee·2024년 4월 30일
1

Python

목록 보기
2/13
post-thumbnail

Lecture 0 - Functions, Variables

Visual Studio 터미널에
$code hello.py 을 입력하면 "hello.py"라는 파이썬 스크립트가 생성된다.

print("hello, world")

$python hello.py == 실행 버튼을 실행하는 대신 터미널로 파이썬 결과값을 볼 수 있다. $(언어)(스크립트 이름)
터미널에 초기화 단축기 == 'clear'

funtions

#input()함수는 사용자의 입력을 기다리는 반짝이는 커서를 나타나게 한다. 그곳에 "input"하면 된다.
input("what's your name?")
print("hello, James")

하지만 input으로 인해 생성된 반짝이는 커서에 Carl 을 입력해도 "hello James"가 출력되는 것을 볼 수 있을 것이다.
쓸모가 없다! 이럴 떄 등장하는 것.

variables

"variables can store values"
숫자, 이름, 이미지, 비디어까지 모든 값을 저장할 수 있다.

name = input("what's your name?")
print("hello, name")

#single equal sign is assignment from right to left

이렇게 작성하면 input 절에 무엇을 입력하든 "hello, name"이 출력된다.

쌍따옴표("") 안에 있는 문장은 모두 string 데이터로 입력하기 때문에,

name으로 배정한 input 값이 아닌 "name"이라는 문장이 그대로 출력된 것이다.

return values

가서 다시 이름 가져와! 명령하면
이름을 들고 오는 기능
포스트잇에 이름을 적어서 다시 가져오는 역할이라고 생각하면 된다.

위 예시에서는 name변수가 return value이다. input("what's your name? ") 에 입력한 이름값이 name변수 안으로 들어가고 그 name변수가 print()절에서 출력된다.

comments

"#"뒤에 코멘트를 작성할 수 있다.
인간을 위한 메모라고 생각하면 된다.
각 code chunk 위에 코멘트를 작성해두면 기억 & 협업에 유리하다.

pseudocode

코멘트를 통해 작성할 수 있다.
프로그래밍의 아웃라인을 작성하는 것.

#step1. 오늘의 목표
#step2. 이름을 물어봐라
#step3. input값 저장 ... 등등

#Ask user for their name
name = input("what's your name? ")

#say hello to user
print("hello,")
print(name)

#print("hello, " + name)
#print("hello,",name)
#위의 코드들은 같은 값을 출력한다.

strings(str)

데이터 타입 중 하나로 "문자열"이라고 부른다.

https://docs.python.org/3/library/functions
https://docs.python.org/3/library/stdtypes.html#string-methods
위 url을 통해 모든 함수들을 찾아보고 공부할 수 있다.
ex) print
print(*objects, sep=' ', end='\n', file=None, flush=False)

문자열 데이터는 보통 쌍따옴표""안에 입력한다. 예시는 다음과 같다.

#Ask user for their name
name = input("what's your name? ")
#print(*objects, sep=' ', end='\n', file=None, flush=False)
#다음 줄로 자동으로 넘어가는 것이 싫다면?
print("hello, ", end="")
print(name)

print함수 논리 구조상 디폴트 값인 백슬래시n을 ""으로 대체한 결과 "hello, name" 이 출력된다.

f-string

#Ask user for their name
name = input("what's your name? ")
#f-stiring! 
print(f"hello, {name}")

f-string
f"hello, {name}"은 f-string이라고 불리는 형식의 문자열입니다. Python 3.6 이상에서 사용할 수 있는 이 형식은 문자열 내에 중괄호 {}를 사용하여 변수의 값을 직접 삽입할 수 있게 해줍니다.

parameters (매개변수)

:함수를 정의할 때 외부로부터 받아들이는 의미의 값.

#Ask user for their name
name = input("what's your name? ")
# Remove whitespace from str
name = name.strip()
# Capitalize user's name
name = name.title()
#special string!
print(f"hello, {name}")

'''
# Capitalize user's name( just the very first letter.. )
name = name.capitalize()
'''

위 코드에서 우리는 input 함수에 james lee 를 입력하면
James Lee 를 돌려받는다. 하지만 이 모든 코드를 훨씬 간단하게 작성할 수도 있다.

name = input("what's your name? ").strip().title()
print(f"hello, {name}")

같은 값을 출력하지만 훨씬 간단한 code chunk이다.
그렇다면 어떤 좋은 코딩이라고 할 수 있을까?
개인 선호도에 따르면 되겠다.

integers(int) 정수

  • "+" 더하기
  • "-" 빼기
  • "*" 곱하기
  • "/" 나누기
  • "%" 나머지 값 출력

interactive mode

터미널을 interactive mode로 열어서 바로 계산을 해 볼 수 있다.

터미널에
$code calculator.py를 작성해서
"calculator.py" 파이썬 스크립트를 생성하여 이어가보자.

x = input("what's x? ")
y = input("what's y? ")
z = x + y
print(z)
#above code chunk concatenates x and y instead of plus operating

위 코드에서 x <- 10, y <- 20 을 배정한 뒤 print(Z)를 실행하면
"1020"이 출력된다.
그 이유는 숫자열끼리의 덧셈이 아닌 문자열끼리의 결합이 실행되었기 때문이다. ("They just join the two strings")
따라서 덧셈을 하기 위해 숫자 변수 x와 y를 정수(int)로 바꿔주어야 한다.

정수변환 후 코딩은 다음과 같다.
1.

#which one is better?
x = input("what's x? ")
y = input("what's y? ")
z = int(x) + int(y)
print(z)

2

#nested function
x = int(input("what's x?"))
y = int(input("what's y?"))
print(x + y)

위 두 개의 code chunk 중 뭐가 더 나은 코딩인지 고민해보자.
하버드 교수 David는 첫 번째 코딩을 선호하는 듯 하다.
"Make your code readable and simple."

float

:소수점이 포함된 숫자들

#float values calculation
x = float(input("what's x? "))
y = float(input("what's y? "))

print(x + y)

rounding

rounding은 반올림으로 이해하면 된다.

# rounding == 반올림 x=1.2 , y=3.4 입력 시 z=5
# round(number[, ndigits])
# [] 안은 number of digits 를 뜻한다. 소수점 n째 자리.
x = float(input("what's x? "))
y = float(input("what's y? "))
z = round(x + y)
print(z)
#나누기 rounding
x = float(input("what's x? "))
y = float(input("what's y? "))
z = round(x / y, 2)
print(z)

1,000 만들기

천 단위에 ,를 찍어보자.

# 1,000 을 출력하는 법?
# x = 999, y = 1, z = 1,000 
x = float(input("what's x? "))
y = float(input("what's y? "))
z = round(x + y)
print(f"{z:,}")

f-string 기능을 활용, 1,000 형식으로 number value(float, integer 모두 가능)를 출력할 수 있다.

def

#input = James , output = hello / James
def hello():
    print("hello")


name = input("what's your name? ")
hello()
print(name)

#예쁘게 한 줄로 대답하는 방법은 없을까? 

정의한 hello()함수에 변수x를 추가하면 다음과 같은 코드를 만들어낼 수 있다.

#input = James , output = "hello, James"
def hello(x):
    print("hello,", x)


name = input("what's your name? ")
hello(name)
#x 자리에 name이 들어간 hello()함수
#"x" 가 value 임

중요한 것은 def 함수가 실행code chunk에 선행되어야 하는 것이다.
hello()가 def hello()보다 선행된다면 "NameError:"가 발생한다.

scope(범위)

def로 만들어낸 함수는 각 기능별 범위를 초과하지 못한다.
하나의 함수를 만들어냈다면 그 함수 내에 있는 변수는 함수 내에서만 기능할 수 있다는 뜻이다.

예를 들어

def main():
    name = input("what's your name? ")
    hello()
	
def hello():
    print("hello,", name)

main()

위 코드를 실행하면 처음에는 what's your name?이 성공적으로 출력되어 input 값에 이름을 줄 수 있게 보이지만, 막상 input값을 입력하면 NameError: name 'name' is not defined 를 보게 될 것이다.

def main():
    name = input("what's your name? ")
    hello(name)

def hello(to="world"):
    print("hello,", to)

main()

따라서 위 코드에서 main() 함수는 main()함수대로, hello()함수는 hello()함수대로 사용해야한다.

return

"return value"

def main():
    x = int(input("what's x? "))
    print("x squared is", square(x))


def square(n):
    return pow(n,2)

main()

#pow() 는 power를 뜻하며, pow(n,4) 는 숫자 n을 4제곱 한다는 뜻이다.
#input = 4, output = 16

Lecture 1 - Conditionals

if

x = int(input("what's x? "))
y = int(input("what's y? "))
if x < y:
    print("x is less than y")
if x > y:
    print("x is greater than y")
if x == y:
    print("x is equal to y")
# if절의 들여쓰기 절은 해당 if문이 "TRUE" 일 때만 출력된다는 것을 의미한다.
# conditionals(조건문) 에서 == 이 equal 을 뜻하며 = 은 assignment 를 뜻한다.

위 code chunk 의 workflow 는 다음과 같다.

if조건문의 진행은 위에서 보이는 것 처럼 작성된 코드의 순서대로 양방향으로 진행된다. if조건문의 결과가 True 이면, 해당 조건문의 print문이 출력되지만, 거기서 멈추지는 않는다=비효율적이다.

elif

: else if

x = int(input("what's x? "))
y = int(input("what's y? "))
if x < y:
    print("x is less than y")
elif x > y:
    print("x is greater than y")
elif x == y:
    print("x is equal to y")
    

"don't keep answering questions once we get back a true answer."
if 문에서 봤던 "true 출력에도 진행이 멈추지 않는 문제"를 해결하기 위해 elif 문을 사용해봤다.
겉으로 봐서는, 그리고 실제로 코드를 실행해봐도 별 차이를 느낄 수 없을 것이다. 하지만 workflow를 보면 차이를 시각적으로 볼 수 있다.

각 조건문에서 true 결과에 따라 조건문이 실행되면 stop되는 것을 볼 수 있다.

else

x = int(input("what's x? "))
y = int(input("what's y? "))

if x < y:
    print("x is less than y")
elif x > y:
    print("x is greater than y")
else:
    print("x is equal to y")

위에 elif를 활용한 비교문을 보면 마지막 elif x ==y: 조건문은 효율적이지 않다. 크지도(>), 작지도(<) 않은데 같지도(=) 않을 경우(!=)는 존재하지 않기 때문이다.

else 문을 활용하여 가장 효율적이고 읽기에 편안한 coding을 할 수 있다. else 문을 활용한 workflow는 다음과 같다.

if, elif, else를 활용한 조건문 작성이 시각적으로, 논리적으로 if만 사용한 조건문 보다 깔끔하다고 할 수 있다.

or

x = int(input("what's x? "))
y = int(input("what's y? "))

if x < y or x > y:
    print("x is not equal to y")
else:
    print("x is equal to y")

!=, ==

x = int(input("what's x? "))
y = int(input("what's y? "))

if x != y:
    print("x is not equal to y")
else:
    print("x is equal to y")

and

$code grade.py

score = int(input("Score: "))

if score >= 90 and score <= 100:
    print("Grade: A")
elif score >= 80 and score < 90:
    print("Grade: B")
elif score >= 70 and score < 80:
    print("Grade: C")
elif score >= 60 and score < 70:
    print("Grade: D")
else:
    print("Grade: F")

위 코드를 통해 각 점수 구간별 학점을 구할 수 있다.
더 효율적인 코딩은 다음과 같다.

score = int(input("Score: "))

if 90 <= score <= 100:
    print("Grade: A")
elif 80 <= score < 90:
    print("Grade: B")
elif 70 <= score < 80:
    print("Grade: C")
elif 60 <= score < 70:
    print("Grade: D")
else:
    print("Grade: F")
#and문 없이 비교연산자만으로 만들어낸 코드, 보기 편하다.
score = int(input("Score: "))

if score >= 90:
    print("Grade: A")
elif score >= 80:
    print("Grade: B")
elif score >= 70:
    print("Grade: C")
elif score >= 60:
    print("Grade: D")
else:
    print("Grade: F")

2번 코드에서 elif를 전부 if로 변경해서 시행하면 95를 입력했을 때
Grade:A 부터 D 까지 전부 출력됨을 알 수 있다. 이는 맨 처음 workflow 시각화 자료에서 본 바와 같이 양 방향으로 코드 출력이 양방향으로 진행되기 때문이다.

+ - * / %

$code parity.py

x = int(input("what's x? "))

if x % 2 == 0:
    print("Even")
else:
    print("Odd")

# %(나머지) 연산을 통해 짝/홀수를 알 수 있다.

bool

: 파이썬이 갖는 value 의 종류
1. str : strins 문자열값
2. int : integers 정수
3. float : floating point values 소수
4. bool : boolean values

bool 값은 딱 두 가지 값을 가진다.
TRUE
FALSE

사용 예시

def main():
    x = int(input("what's x? "))
    if is_even(x):
        print("Even")
    else:
        print("Odd")
#이 상태에서는 is_even() 함수가 정의되어있지 않기 때문에 코드를 실행해도 syntax 혹은 name error가 발생한다.

#is_even 함수 정의
#boolean value
def is_even(n):
    if n % 2 == 0:
        return(True)
    else:
        return(False)

main()

#내가 처음에 작성한 def is_even 절은 다음과 같다... return 과 print 의 차이를 정확하게 알고 가자.
"""
def is_even(n):
    if n % 2 == 0:
        print(True)
    else:
        print(False)
"""

위 코드에서 is_even 절에 boolean value가 작동한다.
1. main() 함수 input == 14
2. 14는 is_even()함수의 n 자리로 들어간다.
3. n 을 2로 나누었을 때 나머지는 0이 되므로 is_even()함수의 결과는 True.
4. True 값을 main()함수로 return하고 main()함수는 "Even"을 출력

매개변수와 인수

def main():
    x = int(input("what's x? "))
    if is_even(x):
        print("Even")
    else:
        print("Odd")


def is_even(n):
    if n % 2 == 0:
        return(True)
    else:
        return(False)

main()
"""
main 함수에서 x는 사용자 입력을 받아 정수로 변환한다.
is_even 함수는 x의 값을 인수로 받아 매개변수 n에 전달한다.
is_even 함수는 n이 짝수인지 홀수인지 검사하고 그 결과를 반환한다.
"""

main 함수 속 is_even(x)
def is_even(n)
괄호 속 x와 n, 왜 달라야 하는가?

여기서 우리는 매개변수와 인수의 차이를 알고 넘어갈 수 있다.

매개변수(parameter): 함수를 정의할 때 사용되는 변수다.
인수(argument): 함수를 호출할 때 함수에 전달되는 실제 값이다.

main 함수에서 x는 사용자 입력을 받아 정수로 변환한다.
is_even 함수는 x의 값을 인수로 받아 매개변수 n에 전달한다.
is_even 함수는 n이 짝수인지 홀수인지 검사하고 그 결과를 반환한다.

여기서 bool을 활용해서 def is_even()함수를 더욱 간단하게 만들어보자.

def is_even(n):
    if n % 2 == 0:
        return(True)
    else:
        return(False)
def is_even(n):
	return True if n % 2 == 0 Else return False
def is_even(n):
	return n % 2 == 0

위 3개의 코드는 모두 같은 값을 출력한다.
마지막 코드가 가장 인상적이라고 할 수 있겠다.
어차피 true 와 false 둘 중 하나의 값만 출력하기에 바로 return하면 된다는 사실을 알고 있는 프로그래머라면 마지막 코드를 작성할 것이다.

return VS print GPT선생의 가르침

처음 코딩할 때 return과 print를 혼용하는 실수를 많이 범했다. 사실 아직도 헷갈려서 gpt한테 설명을 부탁했다.


지금부터는 bool에 예시로 사용한 코드를 예시로 print와 return의 차이를 설명하는 GPT4.0의 답이다.

두 코드 예제에서 is_even 함수는 비슷한 작업을 수행하는 것처럼 보이지만, 실제로는 두 함수가 하는 일이 근본적으로 다릅니다. 이 차이는 함수가 값을 반환하는 방식(return)과 단순히 값을 출력하는 방식(print) 사이에 있습니다.
첫 번째 예제: return 사용


def is_even(n):
    if n % 2 == 0:
        return True
    else:
        return False

이 함수는 주어진 숫자 n이 짝수인지 홀수인지를 검사하고, 그 결과를 True 또는 False로 반환합니다. 이 반환 값은 함수를 호출하는 코드에서 사용될 수 있습니다. 예를 들어, 조건문에서 이 반환값을 기반으로 추가적인 로직을 수행할 수 있습니다.

def main():
    x = int(input("what's x? "))
    if is_even(x):  # is_even(x) 호출 결과에 따라 분기
        print("Even")
    else:
        print("Odd")

여기서 is_even(x) 호출은 x가 짝수인지 판단하고 그 결과를 True 또는 False로 반환합니다. if문은 이 반환값을 사용하여 "Even" 또는 "Odd"를 출력합니다.

두 번째 예제: print 사용

def is_even(n):
    if n % 2 == 0:
        print(True)
    else:
        print(False)

이 함수는 n이 짝수인지 여부를 검사하고 결과를 콘솔에 직접 출력합니다. 그러나 이 함수는 어떠한 값을 반환하지 않습니다(None을 반환하는 것과 동일). 이는 함수의 결과를 다른 곳에서 사용할 수 없다는 의미입니다.

def main():
    x = int(input("what's x? "))
    if is_even(x):  # 여기서 문제 발생
        print("Even")
    else:
        print("Odd")

여기서 is_even(x)는 실제로는 아무것도 반환하지 않기 때문에 (None을 반환), if문은 항상 False로 평가됩니다. 따라서 무조건 "Odd"만 출력하게 됩니다.

왜 return을 사용해야 하는가?
return을 사용하는 것은 함수가 어떤 결과를 계산하고 그 결과를 호출자에게 전달할 필요가 있을 때 중요합니다. return을 사용함으로써 함수는 다음과 같은 이점을 갖게 됩니다:

재사용성: 함수의 결과를 변수에 저장하거나 다른 함수의 입력으로 사용할 수 있습니다.
조건 로직: 함수의 결과에 따라 다른 코드의 실행을 제어할 수 있습니다.
모듈성: 함수가 특정 작업을 완료하고 그 결과를 반환함으로써 코드를 더 모듈화하고 관리하기 쉽게 만듭니다.

print는 단순히 값을 출력할 때 사용되며, 프로그램의 다른 부분과 상호작용하는 데에는 적합하지 않습니다. 따라서 일반적으로 함수 내에서는 결과를 반환하고, 프로그램의 다른 부분에서 그 결과를 출력하는 방식이 더 적절합니다.

그렇다고 한다. 필자는 이해가 됐다.
직접 Visual Studio에 두 가지 코드를 모두 실행해보면 차이를 느낄 수 있을 것이다.

match

해리포터 등장인물들의 house matching 기능을 갖는 코드를 작성해보자

name = input("what's your name? ")

#이 때 name == 을 각각 붙여주지 않으면, 문자열을 입력했을 때 전부 true로 인지하고 Gryffindor 만 출력하게 된다. 
if name == "Harry" or name == "Hermione" or name == "Ron":
    print("Gryffindor")
elif name == "Draco":
    print("Slytherin")
else:
    print("Who? ")

match 함수를 사용해서 다음과 같은 동일한 기능의 코드를 만들 수 있다.

name = input("what's your name? ")

match name:
    case "Harry":
        print("Gryffindor")
    case "Hermione":
        print("Gryffindor")
    case "Ron":
        print("Gryffindor")
    case "Draco":
        print("Slytherin")
#그렇다면 여기에 없는 친구들은? case _ 를 통해 나머지 "else" 의 역할을 수행할 수 있다.
    case _:
        print("Who? ")

"|" 를 사용해서 더 짧게 만들어보자.

name = input("what's your name? ")

match name:
    case "Harry" | "Hermione" | "Ron":
        print("Gryffindor")
    case "Draco":
        print("Slytherin")
    case _:
        print("Who? ")

항상 이렇게 짧고 엘레강스하게 만들 수 있도록 생각하자.

그런데 호그와트에는 다른 이름도 엄청 많은데 이 많은 이름들을 전부 입력할 것인가?
그건 프로그래머가 할 일이 아니다.

Lecture 2 Loops

반복해서 무언가를 출력하고 싶을 때 print()를 계속 사용하는 것은 비효율적이다.

while

print("meow")
print("meow")
print("meow")

meow 를 3번 출력하는 방법은 위의 방법처럼 비효율 적인 방법이 있다.

#i가 0이 아니면 :밑의 지시를 따라라.
i = 3
while i != 0:
    print("meow")

위의 코드를 시행하면 meow가 무한으로 출력된다.
ctrl + c 를 눌러서 멈추고 이제 어떻게 while 문을 사용해야하는지 알아보자.

i = 3
while i != 0:
    print("meow")
    i = i - 1

위의 코드를 실행하면 meow는 3번만 출력하게 된다. workflow는 다음과 같다.

또는 + 연산을 통해 meow를 원하는 만큼 출력할 수 있다. 14번 출력하는 코드를 만들어보겠다.

i = 0
while i < 14:
    print("meow")
    i += 1
    # i = i + 1 식을 축약 표현한 것.

for

#for i in [0, 1, 2]: 비효율적인 코드
for i in range(10):
	print("meow")

range()를 사용하여 10개의 meow를 출력하는데 0 ~ 10까지 작성하지 않고도 출력할 수 있다.
"i" 라는 변수를 정해두고 아예 사용하지 않는데, 이는 파이썬 구조가 변수 자리를 요구하기 때문이다. 따라서 i 대신에 _ 를 넣어놔도, 어떤 변수를 넣어놔도 상관없다.

for _ in range(10):
for i in range(10):
위 두 개의 코드는 차이가 없다.

"meow"를 10번 출력하는 다른 방법은 뭐가 있을까?

print("meow" * 10)

위 코딩의 결과 "meowmeowmeow....." 처럼 가로로 쭉 늘어진 meow가 출력될 것이다.
이러한 문제를 해결하기 위해선 print()함수의 디폴트 구조를 알고 있어야 한다.
https://docs.python.org/3/library/functions.html#print
print(*objects, sep=' ', end='\n', file=None, flush=False)

위에 보이는 print()함수 디폴트 구조를 참고하여 다시 코딩을 해보자.

print("meow\n" * 10, end="")

백슬래시n 은 "다음 행으로 이동" 이라는 기능을 수행한다.
end='백슬래시n' 대신 end=''을 넣어서 print()함수가 끝났을 때 다음 행으로 넘어가지 말고 그냥 끝나라고 명령하면

meow
meow
.
.
.
meow

위 구조로 출력할 수 있게 된다.
이런 방법으로 반목문을 한 줄 코딩 할 수 있지만
앞서 공부한 for, while loop도 숙지하도록 하자.

무한루프와 탈출

유저에게 값을 제공받아 그만큼 반복되는 출력값을 받아내는 가장 전형적인 코드는 다음과 같다.

# while 반복문과 for 반복문을 활용한 루프탈출, 및 meow 출력
# while True: True에 대한 답은 어떤 답이든 항상 true이기 때문에 무한루프를 생성한다. 
# 무한루프는 break 문을 만나기 전까지 계속된다.
while True:
    n = int(input("what's n? "))
    if n > 0:
        break

for _ in range(n):
    print("meow")

# while 문의 루프가 break문으로 인해 탈출되면 for 문이 시작된다.

여기서 break문은 아무리 들여쓰기가 되어 있다고 하더라고 가장 최근 while loop를 탈출하는 역할을 한다.
위 코드의 수행 절차는 다음과 같다.
1. while True: 무한루프 생성
2. n 물어보기
3. n 의 양수 여부 점검
4. 3의 결과에 따라 루프 탈출 후 다음(for문) 절차 진행
5. for문의 결과 meow n번 출력.

for 문을 활용한 meow 출력

#def 를 활용한 meow 3번 출력

def main():
    meow(3)

def meow(n):
    for _ in range(n):
        print("meow")

main()
#for 문의 구조: for _ in range() 를 숙지해야 한다.

코딩 초보자는 for문의 구조를 외워두는게 도움이 될 것 같다.
위 코드는 meow() 괄호 안에 입력한 숫자만큼의 moew만 출력하는데 유저에게 몇 번 출력하고 싶은지 물어보는 코드를 짜보자.

유저가 원하는 만큼 meow 출력하기

#원하는 값을 입력하여 출력
def main():
    number = get_number()
    meow(number)
    
def get_number():
    while True:
        n = int(input("what's n? "))
        if n > 0:
                break
    return n
#break 대신에 바로 return을 사용해도 되지만 이해를 돕기 위해 break 후 return을 작성

def meow(n):
    for _ in range(n):
        print("meow")

main()

#8번 라인에 무한루프를 굳이 생성해야하나..?

굳이 무한루프를 생성하는 이유는 무엇일까.
답: 사용자로부터 유효한 입력, 즉 양수를 받을 때까지 계속 입력을 받기 위함이다.

def main():
    number = get_number()
    meow(number)

def get_number():
    n = int(input("what's n? "))
    if n > 0:
        return n


def meow(n):
    for _ in range(n):
        print("meow")

main()

위 코드에 양수값을 입력해도 잘 출력된다.
input: 3
output: meow 3번 출력.

하지만 음수값을 입력하면 error가 발생한다.

이런 문제는 while True 무한루프로 해결 가능하다.
"유저가 원하는 만큼 meow 출력하기" 코드에서 보이는 것처럼 무한루프 코딩을 양수를 받을 때까지 계속 물어본다.

list

: 파이썬이 갖는 value 의 종류
1. str : strins 문자열값
2. int : integers 정수
3. float : floating point values 소수
4. bool : boolean values
5. list : multiple values all in the same place. 현실에서 육류(str), 채소(int) 등등을 하나의 장바구니(list)에 담는다고 이해하면 된다.

# list 출력 []은 리스트를 감싸는 기호
# [a,b,c]의 자리수는 0,1,2 가 된다. 각 자리수를 출력하는 예시는 다음과 같다.
students = ["해리", "해르미온느", "론"]

print(students[0])
print(students[1])
print(students[2])

만약 위 코드에서 print(students)를 그대로 실행하면 리스트 전체가 출력된다 == ['해리', '해르미온느', '론']
우리는 리스트 내 각 자릿수에 해당하는 값을 원하기에 students[자릿수]를 사용하여 print하는 것이다.

for 문을 통한 자동화 결과는 다음과 같다.

students = ["해리", "해르미온느", "론"]

for student in students:
    print(student)

for 문의 "student" 는 모든 변수로 교체 가능하다. student 말고 a, b, s, _ 등등 어떤 값을 넣어도 된다는 것을 염두하자. 여기선 이해를 위해 students 라는 list 이름을 고려하여 student로 설정했다.
코드를 다른 모든 사람들(미래의 나를 포함)도 읽을 수 있다록 설계해라.

len

length of a list 를 확인해서 변환
ex) len[a,b,c,e,d] == 5

list를 활용한 len함수 사용예시

students = ["해리", "해르미온느", "론"]
for i in range(len(students)):
    print(i + 1, students[i])
    #print함수는 " "로 구분되는 2개의 변수를 출력할 수 있다.
  1. []를 통한 리스트 선언
  2. len함수를 통해 3을 생성
  3. range(3) 을 통해 0,1,2 생성
  4. students[i]는 리스트 students의 i번째 원소를 참조합니다. i가 0일 때 "해리", 1일 때 "해르미온느", 2일 때 "론"을 출력합니다.

dict

:dictionary
key 와 value
list는 value들의 나열이다.
반면 dictionary는 2차원의 배열이다.

[ ] 대신 { }을 사용할 것이다.

students = {
    "Hermione" : "Gryffindor",
    "Harry" : "Gryffindor",
    "Ron" : "Gryffindor",
    "Draco" : "Slytherin"
}

print(students["Hermione"])

왼쪽에 key, 오른쪽에 value 인 dict을 생성했다.
print에 key를 input으로 주면 해당 key에 대응하는 value를 output을 받는다.

students = {
    "Hermione" : "Gryffindor",
    "Harry" : "Gryffindor",
    "Ron" : "Gryffindor",
    "Draco" : "Slytherin"
}

for student in students:
    print(student)

해당 for 문을 실행하면, key값들만 출력된다.

students = {
    "Hermione" : "Gryffindor",
    "Harry" : "Gryffindor",
    "Ron" : "Gryffindor",
    "Draco" : "Slytherin"
}

for student in students:
    print(student, students[student], sep=", ")

위 for 문을 실행하면, key 값과 해당 key에 해당하는 value를 같이 출력한다. 또한 sep기능을 통해 가독성을 높은 output을 준다.

for 가 반복문임을 기억하라.
for student in students:
students 딕셔너리는 키-값 쌍으로 이루어져 있습니다. 여기서 키는 학생의 이름이고 값은 그 학생의 하우스입니다.
for 루프는 students 딕셔너리의 모든 키(즉, 학생의 이름)를 순회합니다.
student 변수는 반복할 때마다 딕셔너리의 다음 키를 가리킵니다.
print(student, students[student], sep=", ")
print 함수는 주어진 값을 출력합니다.
print 함수의 첫 번째 인수는 student로, 현재 반복 중인 학생의 이름입니다.
print 함수의 두 번째 인수는 students[student]로, 현재 학생의 이름을 키로 사용하여 그 학생의 하우스를 딕셔너리에서 가져옵니다.
sep 매개변수는 출력할 항목들 사이에 넣을 문자열을 지정합니다. 기본값은 공백(' ')이지만, 여기서는 쉼표와 공백(', ')으로 설정되었습니다.

키 하나에 여러가지 값을 연결하는 dictionary를 만들고자 한다!
리스트 안에 dict을 넣는 방법으로!

students = [
    {"name": "Hermione", "house": "Gryffindor", "patronus": "Otter"},
    {"name": "Harry", "house": "Gryffindor", "patronus": "Stag"},
    {"name": "Ron", "house": "Gryffindor", "patronus": "Jack Russell terrier"},
    {"name": "Draco", "house": "Slytherin", "patronus": None}
]

for student in students:
    print(student["name"], student["house"], student["patronus"], sep=", ")

#for문을 통해 students 리스트 전부를 훑어낼것이다!
#그 중 Key에 해당하는 값들을 사전에서 하나씩 찾아낸다.

create mario block

마리오 게임을 만들어보자.
code mario.py

이 블럭을 만들기 위한 함수는?

print("#")
print("#")
print("#")
for _ in range(3):
    print("#")
def main():
    print_column(3)
#일단 할 수 있다고 치고 print_column을 사용한 뒤 뒤에 해당 함수를 만들어준다.
def print_column(height):
    for _ in range(height):
        print("#")
main()
def main():
    print_column(3)
def print_column(height):
    print("#\n" * height, end="")
main()


이 블록을 만들기 위한 함수는?

def main():
    print_row(4)
def print_row(width):
    print("?" * width)
main()

왜 이렇게 비효율적으로 블록을 만들어 내냐는 의문이 들 수 있다. 답은 다음에 나온다.

3*3 블록은 어떻게 만들겨!!!

def main():
    print_square(3)
def print_square(size):
    # For each row in square
    for i in range(size):
        # For each brick in row
        for j in range(size):
            # Print brick
            print("#", end="")
        # 내부 루프 끝난 후 줄 바꿈 실시
        print()
main()
#print(*objects, sep=' ', end='\n', file=None, flush=False)
첫 번째 코드: 가장 비효율적입니다. 
불필요하게 중첩된 루프를 사용하여 복잡성을 증가시킵니다.
첫 번째 코드를 시각화하여 설명하겠습니다. 
이 코드는 중첩된 for 루프를 사용하여 정사각형 패턴을 출력합니다. 
각 루프와 출력 과정을 시각적으로 나타내 보겠습니다.

시각화

  1. main 함수 호출

    • main 함수가 호출되면 print_square(3)을 실행합니다.
  2. print_square 함수 실행

    • print_square 함수가 호출되고, size3으로 설정됩니다.
    • for i in range(size) 루프가 시작됩니다. 이 루프는 i0, 1, 2일 때 실행됩니다.
  3. 첫 번째 외부 루프 반복 (i = 0)

    • for j in range(size) 루프가 시작됩니다. 이 루프는 j0, 1, 2일 때 실행됩니다.

    • j = 0: print("#", end="")#을 출력하고 줄 바꿈을 하지 않습니다.

      #
    • j = 1: print("#", end="")#을 출력하고 줄 바꿈을 하지 않습니다.

      ##
    • j = 2: print("#", end="")#을 출력하고 줄 바꿈을 하지 않습니다.

      ###
    • 내부 루프가 끝난 후 print()가 호출되어 줄 바꿈이 발생합니다.

      ###
      
  4. 두 번째 외부 루프 반복 (i = 1)

    • for j in range(size) 루프가 다시 시작됩니다.

    • j = 0: print("#", end="")#을 출력하고 줄 바꿈을 하지 않습니다.

      ###
      #
    • j = 1: print("#", end="")#을 출력하고 줄 바꿈을 하지 않습니다.

      ###
      ##
    • j = 2: print("#", end="")#을 출력하고 줄 바꿈을 하지 않습니다.

      ###
      ###
    • 내부 루프가 끝난 후 print()가 호출되어 줄 바꿈이 발생합니다.

      ###
      ###
      
  5. 세 번째 외부 루프 반복 (i = 2)

    • for j in range(size) 루프가 다시 시작됩니다.

    • j = 0: print("#", end="")#을 출력하고 줄 바꿈을 하지 않습니다.

      ###
      ###
      #
    • j = 1: print("#", end="")#을 출력하고 줄 바꿈을 하지 않습니다.

      ###
      ###
      ##
    • j = 2: print("#", end="")#을 출력하고 줄 바꿈을 하지 않습니다.

      ###
      ###
      ###
    • 내부 루프가 끝난 후 print()가 호출되어 줄 바꿈이 발생합니다.

      ###
      ###
      ###
      

최종 출력

###
###
###

요약

  • 외부 루프(for i in range(size)): 정사각형의 각 행을 처리합니다.
  • 내부 루프(for j in range(size)): 각 행의 각 열을 처리하여 #을 출력합니다.
  • print("#", end=""): #을 출력하되, 줄 바꿈을 하지 않습니다.
  • 내부 루프가 끝난 후 print(): 행의 끝에서 줄 바꿈을 합니다.

이 과정을 통해 3x3 정사각형 패턴이 출력됩니다.

def main():
    print_square(3)
def print_square(size):
    for i in range(size):
        print("#" * size)
main()

두 번째 코드: 효율적이고 간결합니다. 중첩 루프 없이 한 줄의 코드를 사용하여 각 행을 출력합니다.
두 번째 코드를 시각화하여 설명하겠습니다. 이 코드는 단일 for 루프를 사용하여 정사각형 패턴을 출력합니다. 각 루프와 출력 과정을 시각적으로 나타내 보겠습니다.

시각화

  1. main 함수 호출

    • main 함수가 호출되면 print_square(3)을 실행합니다.
  2. print_square 함수 실행

    • print_square 함수가 호출되고, size3으로 설정됩니다.
    • for i in range(size) 루프가 시작됩니다. 이 루프는 i0, 1, 2일 때 실행됩니다.
  3. 첫 번째 루프 반복 (i = 0)

    • print("#" * size)가 실행됩니다.
    • "#" * size"#"size만큼 반복합니다. 즉, "#" * 3"###"가 됩니다.
    • 따라서, "###"가 출력됩니다.
      ###
  4. 두 번째 루프 반복 (i = 1)

    • print("#" * size)가 다시 실행됩니다.
    • "#" * size는 다시 "###"가 되어 출력됩니다.
      ###
      ###
  5. 세 번째 루프 반복 (i = 2)

    • print("#" * size)가 다시 실행됩니다.
    • "#" * size는 다시 "###"가 되어 출력됩니다.
      ###
      ###
      ###

최종 출력

###
###
###

요약

  • for i in range(size) 루프: 정사각형의 각 행을 처리합니다.
  • print("#" * size): "#"size만큼 반복하여 출력합니다.

이 과정을 통해 3x3 정사각형 패턴이 출력됩니다. 코드가 단순하여 이해하기 쉽고, 불필요한 중첩 루프 없이 간결하게 작성되었습니다.

def main():
    print_square(3)
def print_square(size):
    for i in range(size):
        print_row(size)
def print_row(width):
    print("#" * width)
main()

세 번째 코드: 가장 효율적입니다. 코드의 재사용성을 높이기 위해 함수를 분리했습니다. print_row 함수를 별도로 정의함으로써 코드의 모듈성을 개선하고, 필요할 경우 다른 곳에서도 재사용할 수 있게 했습니다.

시각화

  1. main 함수 호출

    • main 함수가 호출되면 print_square(3)을 실행합니다.
  2. print_square 함수 실행

    • print_square 함수가 호출되고, size3으로 설정됩니다.
    • for i in range(size) 루프가 시작됩니다. 이 루프는 i0, 1, 2일 때 실행됩니다.
  3. 첫 번째 루프 반복 (i = 0)

    • print_row(size)가 호출됩니다.
    • size3이므로 print_row(3)가 호출됩니다.
    • print_row 함수는 width3으로 설정됩니다.
    • print("#" * width)가 실행됩니다. "#" * 3"###"가 됩니다.
    • 따라서, "###"가 출력됩니다.
      ###
  4. 두 번째 루프 반복 (i = 1)

    • print_row(size)가 다시 호출됩니다.
    • print_row(3)가 호출됩니다.
    • print_row 함수에서 print("#" * width)가 실행되어 "###"가 출력됩니다.
      ###
      ###
  5. 세 번째 루프 반복 (i = 2)

    • print_row(size)가 다시 호출됩니다.
    • print_row(3)가 호출됩니다.
    • print_row 함수에서 print("#" * width)가 실행되어 "###"가 출력됩니다.
      ###
      ###
      ###

최종 출력

###
###
###

요약

  • main 함수에서 print_square 함수가 호출됩니다.
  • print_square 함수는 for i in range(size) 루프를 통해 각 행을 처리합니다.
  • 각 행마다 print_row(size)가 호출됩니다.
  • print_row 함수는 print("#" * width)를 통해 각 행을 출력합니다.

세 번째 코드의 구조

  1. main 함수

    • print_square(3) 호출
  2. print_square 함수

    • size만큼 반복하며 print_row(size) 호출
  3. print_row 함수

    • width 만큼 "#" 출력

이 과정은 코드의 모듈성을 높이고, 재사용 가능한 함수로 분리함으로써 코드의 가독성과 유지 보수성을 향상시킵니다.

Lecture 3 - Exceptions

SyntaxError

ValueError

x = int(input("what's x? "))
print(f"x is {x}")
#뭐가 잘못되었을까?
#어떻게 고쳐야 하나?

input 문에 숫자를 넣으면 아무런 문제가 없지만 "cat"과 같은 string을 넣게 되면 value error가 발생한다.

try

except

try:
    x = int(input("what's x? "))
    print(f"x is {x}")
except ValueError:
    print("x is not an integer")

try와 except를 사용하면, 에러 사인을 내가 원하는 메세지로 대체할 수 있다.

NameError

try:
    x = int(input("what's x? "))
except ValueError:
    print("x is not an integer")

print(f"x is {x}")

NameError: name 'x' is not defined
이 name에러는 왜 발생했을까?
indent되어있지 않은 print절은 input에서 배정받은 x와는 다른 상태의 x를 갖는다.

else

try:
    x = int(input("what's x? "))
except ValueError:
    print("x is not an integer")
else:
    print(f"x is {x}")

loop

while True:
    try:
        x = int(input("what's x? "))
    except ValueError:
        print("x is not an integer")
    else:
        break

print(f"x is {x}")
#treu is true. so this is the infinite loop
#이 무한루프는 x로 int값이 제공될때까지 계속 what's x? 를 물어본다.

break를 사용하지 않고 print를 사용하면, print 후에도 계속 물어본다. 영원히.
또한 error가 발생하는 곳은 int(input("what's x?"))
이기 때문에 break는 4줄에 위치해도 상관없다. 성공적으로 break하기만 된다.

def main():
    x = get_int()
    print(f"x is {x}")


def get_int():
    while True:
        try:
            x = int(input("what's x? "))
        except ValueError:
            print("x is not an integer")
        else:
            break
    return x

main()

get_int함수의 기능을 쌈뽕하게 설계했다.
어 짧게 만들어볼까?

def main():
    x = get_int()
    print(f"x is {x}")
def get_int():
    while True:
        try:
            x = int(input("what's x? "))
        except ValueError:
            print("x is not an integer")
        else:
            return x 
main()
def main():
    x = get_int()
    print(f"x is {x}")
def get_int():
    while True:
        try:
            return int(input("what's x? "))
        except ValueError:
            print("x is not an integer")
main()

전부 가능한 코드들.

pass

말 글대로 패스하는 기능. 계속 x is not an integer 을 보이는 것 보다 젠틀하게 인간을 대하는 법

1번 코드

def main():
    x = get_int()
    print(f"x is {x}")
def get_int():
    while True:
        try:
            return int(input("what's x? "))
        except ValueError:
            pass
main()
#indentation is what is associated with what

그냥 계속 물어보기만 하는 코드로 바뀐다. what's x?

위 코드는 아래 코드와 정확히 같은 방식으로 작동한다.

2번 코드

def main():
    x = get_int("what's x? ")
    print(f"x is {x}")
def get_int(prompt):
    while True:
        try:
            return int(input(prompt))
        except ValueError:
            pass
main()

1번 코드와 2번 코드 비교
유연성:
첫 번째 코드: get_int 함수는 하드코딩된 프롬프트 문자열 "what's x? "을 사용합니다. 이 함수는 다른 입력 메시지를 사용해야 할 경우 수정이 필요합니다.
두 번째 코드: get_int 함수는 인수로 프롬프트 문자열을 받아서, 입력 메시지를 유연하게 변경할 수 있습니다. 따라서 재사용성이 더 높습니다.
재사용성:
첫 번째 코드: get_int 함수는 특정 상황에서만 사용될 수 있습니다.
두 번째 코드: get_int 함수는 다양한 상황에서 사용할 수 있습니다. 예를 들어, 다른 프롬프트 메시지와 함께 사용될 수 있습니다.
가독성:
두 코드 모두 가독성이 좋습니다. 하지만 두 번째 코드가 조금 더 직관적입니다. 왜냐하면 프롬프트 메시지를 함수 인수로 전달하기 때문에 함수의 유연성이 명확하게 드러나기 때문입니다.

2번 코드 재사용 예시

def main():
    age = get_int("Enter your age: ")
    year = get_int("Enter the current year: ")
    print(f"You are {age} years old and the current year is {year}")
def get_int(prompt):
    while True:
        try:
            return int(input(prompt))
        except ValueError:
            pass
main()

raise

Lecture 4 - Libraries

modules

resusablity of code를 위한 것.
복사 붙여넣기 하지 않아도 된다!

random

docs.python.org/3/library/random.html
난수 발생하기, 코인 던지기, 카드 섞기 등에 사용 가능한 library

import

모듈을 가져오는 역할.

Statistics Libraries
R프로그래밍의 package와 같은 개념이다.

sys

docs.python.org/3/library/sys.html

sys.argv : argument vector

random 모듈을 설치한다!

import random
import random

coin = random.choice(["heads", "tails"])
#두 개 중 하나를 랜덤으로 골라버린다잉! 그래서 choice임.
print(coin)

from

import를 더 상세하게 사용할 수 있는 기능
import는 import everything 임

from random import choice
#내가 사용하고자 하는 기능의 범위를 줄이는 것이다.
coin = choice(["heads", "tails"])
print(coin)

random.choice를 계속 쓰는건 하수다!
from 절에서 올려두고 choice만 사용해~

random.randint(a, b)

import random

number = random.randint(1, 10)
print(number)

출력값이 1~10 사이의 int값이다.

random.shuffle(x)

말 그대로 섞는 것! 카드를 섞는 것에 사용 가능
리스트 안에 있는 값을 섞는다.

import random

cards = ["jack", "queen", "king"]
random.shuffle(cards)
#그냥 프린트 하는 것 보다 하나씩 뽑아내고 싶다!
for card in cards:
	print(card)

statistics labrary

average, mean, medium ...
statistics labrary doc

import statistics

print(statistics.mean([100, 90]))

command-line arguments

input에 값을 넣는 것 보다. 커멘드 라인에 넣는 것? 잘 모르겠네.

sys

system itself
sys library doc

sys.argv

argument vector
값은 list이다.

import sys

print("hello, my name is", sys.argv[1])

터미널 창에
python name.py James
를 실행시키면
hello my name is James 가 튀어나온다.
터미널 창에 바로 값을 입력하는 것으로 추정됨.
py 뒤에 값을 입력하지 않으면 IndexError 가 발생한다.

그렇다면 [1] 은 뭘 의미할까?
위치를 의미한다.

앞서 말한 IndexError를 해결하는 방법은?
학생 답: 1 자리에 뭐가 없으면 ~ 을 출력한다 라는 조건을 건다

import sys

try:
	print("hello, my name is", sys.argv[1])
except IndexError:
	print("Too few argumanets")

이제 교수의 방법

import sys

if len(sys.argv) < 2:
	print("Too few argumnets")
elif len(sys.argv) > 2:
	print("Too many argumnets")
else:
	print("hello, my name is", sys.argv[1])

이 민 규 -> 너무 길다!
철 수 -> 너무 길다!
존 -> 존
none -> 너무 짧다!

근데 ""괄호 안에 풀네임을 입력하면 파이썬은 ""괄호 안에 있는 값들을 하나의 덩어리로 인지하기에 전부 출력된다. 이 때 괄호는 제거되어서 출력된다.

import sys

# Check for errors
if len(sys.argv) < 2:
	print("Too few argumnets")
elif len(sys.argv) > 2:
	print("Too many argumnets")

# Print name tags
print("hello, my name is", sys.argv[1])

여기서 어떤 에러가 발생할까?
sys.argv[1] 때문에 index error가 발생한다.
왜?

sys.exit

import sys

if len(sys.argv) < 2:
	sys.exit("Too few argumnets")
elif len(sys.argv) > 2:
	sys.exit("Too many argumnets")

print("hello, my name is", sys.argv[1])

위 코드는 정상작동한다.
이유는?
sys.exit 알아봐라.

sys.exit()는 파이썬의 sys 모듈에 정의된 함수로, 스크립트의 실행을 즉시 중지하고 종료하는 역할을 합니다². 특정 시점에서 강제로 종료를 해줘야 할 때 사용됩니다¹.
sys.exit()의 주요 특징은 다음과 같습니다:

  • sys.exit()는 종료 코드를 반환합니다. 종료 코드의 기본값은 0입니다¹.
  • sys.exit(1)과 같이 종료 코드 값을 설정해줄 수 있습니다¹.
  • return은 함수의 종료를 의미하므로 스크립트 전체가 종료되지 않지만, sys.exit()는 스크립트 전체를 종료할 수 있습니다¹.
    사용 예시는 다음과 같습니다:
import sys
a, b = 10, 20
sys.exit(1)  # 종료 코드를 1로 설정
print(a, b)  # 스크립트가 종료되었기 때문에 a, b 값을 출력하지 않음

위의 코드에서 sys.exit(1)이 호출되면, 그 이후의 코드는 실행되지 않고 스크립트가 종료됩니다¹.

slices

of a list
subset of data

import sys
if len(sys.argv) < 2:
	sys.exit("Too few arguments")

for arg in sys.argv[1:]:
	print("hello, my name is", arg)

packages

3rd party libraries out there.
어디든 설치해서 사용할 수 있는 패키지!

PyPi

PyPi 사이트

cowsay

cowsay

pip

terminal window
pip install cowsay
를 입력하면 cowsay 가 설치된다.

import cowsay
import sys

if len(sys.argv) == 2:
	cowsay.cow("hello, " + sys.argv[1])

import cowsay
import sys

if len(sys.argv) == 2:
	cowsay.trex("hello, " + sys.argv[1])

요런 재밌는 것을 할 수 있다!

APIs

파이썬에만 국한되는 것이 아닌 개념

requests library

이 라이브러리를 통해 API를 가져올 수 있다
requests

터미널에 pip install requests 를 작성해서 라이브러리를 설치한 뒤
code itunes.py 를 작성해서 파이썬 파일을 생성하자.
(https://itunes.apple.com/search?entity=song&limit=1&term=weezer)

애플 API에 접근하는 것임.
entity=song 을 통해 albums, vedios 가 아니라 song을 원한다고 알려줌
limit=1 으로 1개의 노래를. 등등의 정보로 제한을 둔다.

txt 파일이 다운됨.

{
"resultCount":1,
"results": [
{"wrapperType":"track", "kind":"song", "artistId":115234, "collectionId":1440878798, "trackId":1440879325, "artistName":"Weezer", "collectionName":"Weezer", "trackName":"Buddy Holly", "collectionCensoredName":"Weezer", "trackCensoredName":"Buddy Holly", "artistViewUrl":"https://music.apple.com/us/artist/weezer/115234?uo=4", "collectionViewUrl":"https://music.apple.com/us/album/buddy-holly/1440878798?i=1440879325&uo=4", "trackViewUrl":"https://music.apple.com/us/album/buddy-holly/1440878798?i=1440879325&uo=4",
"previewUrl":"https://audio-ssl.itunes.apple.com/itunes-assets/AudioPreview221/v4/24/d0/07/24d007fc-6bd8-618b-618b-ae5f431ee89c/mzaf_1481529079228661859.plus.aac.p.m4a", "artworkUrl30":"https://is1-ssl.mzstatic.com/image/thumb/Music221/v4/d0/16/da/d016da24-577e-b584-3a5a-116efb5ca362/16UMGIM52971.rgb.jpg/30x30bb.jpg", "artworkUrl60":"https://is1-ssl.mzstatic.com/image/thumb/Music221/v4/d0/16/da/d016da24-577e-b584-3a5a-116efb5ca362/16UMGIM52971.rgb.jpg/60x60bb.jpg", "artworkUrl100":"https://is1-ssl.mzstatic.com/image/thumb/Music221/v4/d0/16/da/d016da24-577e-b584-3a5a-116efb5ca362/16UMGIM52971.rgb.jpg/100x100bb.jpg", "collectionPrice":10.99, "trackPrice":1.29, "releaseDate":"1994-02-28T12:00:00Z", "collectionExplicitness":"notExplicit", "trackExplicitness":"notExplicit", "discCount":1, "discNumber":1, "trackCount":10, "trackNumber":4, "trackTimeMillis":159587, "country":"USA", "currency":"USD", "primaryGenreName":"Pop", "isStreamable":true}]
}

JSON

위에서 보이는 txt 파일처럼 {}.[],등등으로 구조화되어 있는 txt 파일 이다.

import requests
import sys

if len(sys.argv) != 2:
	sys.exit()

response = requests.get("https://itunes.apple.com/search?entity=song&limit=1&term=" + sys.argv[1])
print(response.json())

터미널에
python itunes.py weezer 를 입력하면
앞서 보여준 json txt파일이 터미널에 나타난다.

파이썬은 파이썬 dictonary를 원한다.

python-json docs

import json
import requests
import sys

if len(sys.argv) != 2:
	sys.exit()

response = requests.get("https://itunes.apple.com/search?entity=song&limit=1&term=" + sys.argv[1])
print(json.dumps(response.json(), indent=2))


구조가 더 잘 보인다.
https://www.youtube.com/watch?v=nLRL_NcnK-4&t=17786s
5:55:00 부터 보면 된다.

dictionary 는 key-value pair 로 이루어져 있다.
위 결과값은 하나의 dictionary. 여러 쌍의 pair로 구성되어 있다.

trackName key에 해당하는 value를 가지고 싶다면?

import json
import requests
import sys

# 인수 길이 확인: 스크립트 이름 포함 두 개여야 함
if len(sys.argv) != 2:
    sys.exit()

# API 요청 보내기
response = requests.get("https://itunes.apple.com/search?entity=song&limit=50&term=" + sys.argv[1])

# 응답을 JSON으로 변환
o = response.json()

# 결과 출력
for result in o["results"]:
    print(result["trackName"])

지금까지 외부 라이브러리를 통해 실제 데이터를 다루는 방법을 알아봤다.

스스로의 코드를 생성해 보는 것이 가장 좋은 연습이다.
bundling up your own code

시나리오
1. saying.py 생성 후 def함수 생성
2. say.py 생성 후 saying.py에서 생성한 함수 가져와서 사용하기

code saying.py

def main():
    hello("world")
    goodbye("world")



def hello(name):
    print(f"hello, {name}")


def goodbye(name):
    print(f"goodbye, {name}")


main()

code say.py

import sys

from sayings import hello

if len(sys.argv) == 2:
    hello(sys.argv[1])

python say.py James 을 터미널에 입력하여 실행하면,
hello, James가 나오는 것이 아니라. saying.py 파일의 main()절이 통째로 불려와 실행된 뒤 hello함수만 따로 한 번 더 실행된다. 터미널에 출력되는 결과는 다음과 같다.

hello, world
goodbye, world
hello, world

--name-- (underscore_임)

특별함수! command 라인에서 코드를 실행할 때를 위한 특별함수다. ?
Python에서 __name__은 모듈의 이름을 나타내는 내장 변수입니다. 이 변수는 두 가지 주요 상황에서 사용됩니다.

  1. 현재 스크립트가 직접 실행되는 경우:
    • Python 인터프리터에 의해 스크립트가 직접 실행되면 __name__ 변수는 "__main__"으로 설정됩니다.
    • 이는 스크립트가 직접 실행될 때 특정 코드를 실행하도록 하는 데 유용합니다.
  2. 다른 모듈에서 임포트되는 경우:
    • 스크립트가 모듈로서 다른 스크립트에 임포트되면 __name__ 변수는 모듈의 이름으로 설정됩니다.
    • 이를 통해 모듈이 임포트될 때 특정 코드를 실행하지 않도록 할 수 있습니다.

다음은 이를 설명하는 예제 코드입니다:

# example.py

def main():
    print("이 스크립트는 직접 실행되었습니다.")

if __name__ == "__main__":
    main()
else:
    print("이 스크립트는 임포트되었습니다.")

이 스크립트를 직접 실행하면 __name__"__main__"으로 설정되므로 다음과 같은 결과를 출력합니다:

이 스크립트는 직접 실행되었습니다.

그러나 다른 스크립트에서 이 모듈을 임포트하면 __name__"example"으로 설정되므로 다음과 같은 결과를 출력합니다:

# 다른 스크립트에서 example.py를 임포트하는 경우

import example
이 스크립트는 임포트되었습니다.

이렇게 __name__을 사용하면 스크립트가 직접 실행될 때와 임포트될 때의 동작을 구분할 수 있습니다. 이는 모듈의 재사용성을 높이고, 테스트 및 디버깅을 용이하게 하는 데 매우 유용합니다.

따라서 sayings.py 코드는 아래와 같아야 한다.

def main():
    hello("world")
    goodbye("world")



def hello(name):
    print(f"hello, {name}")


def goodbye(name):
    print(f"goodbye, {name}")

if __name__ == "__main__":
    main()

Lecture 5 - Unit Tests

code calculator.py

def main():
    x = int(input("what's x? "))
    print("x squared is ", square(x))

def square(n):
    return n * n


if __name__ == __main__:
	main()

code test_calculator.py

from calculator import square


def main():
    test)square()


def test_square():
    if square(2) != 4:
        print("2 squared was not 4")
    if square(3) != 9:
        print("3 squared was not 9")

#main() 절을 import되지 않은 경우에만 그대로 출력하겠다! 
if __name__ == "__main__":
    main()

test_calculator.py 를 실행하면 아무것도 일어나지 않는다.
나쁘지 않다.
그렇다면 calculator.py 6번째 줄을 return n + n 으로 바꿔보자.

calculator.py (n * n 에서 n + n 으로 바꿈)

def main():
    x = int(input("what's x? "))
    print("x squared is ", square(x))

def square(n):
    return n + n


if __name__ == "__main__":
    main()

test_calculator.py (그대로)

from calculator import square


def main():
    test_square()


def test_square():
    if square(2) != 4:
        print("2 squared was not 4")
    if square(3) != 9:
        print("3 squared was not 9")


if __name__ == "__main__":
    main()

test_calculator.py 를 실행하면
"3 squared was not 9" 이 출력된다.

    if square(3) != 9:
        print("3 squared was not 9")

위 코드에서 에러가 발생한 것인데 그렇다면 왜 그 위에 있는

    if square(2) != 4:
        print("2 squared was not 4")

는 그냥 통과한 것일까?

2 + 2 와 2 * 2의 값이 동일하기 때문이다.

그런데 항상 이렇게 하나씩 코드를 짜 가면서 오류를 찾기에는 우리가 너무 똑똑하다. 이때 등장하는 기능.

assert

Python에서 assert 문은 프로그램의 특정 조건이 참인지 확인하는 데 사용됩니다. 조건이 거짓이면, assert 문은 AssertionError 예외를 발생시키고, 프로그램의 실행을 중단합니다. 주로 디버깅 목적으로 사용됩니다.

assert 를 사용해서 tast_calculator.py 파일을 바꿔보자.

from calculator import square


def main():
    test_square()


def test_square():
    assert square(2) == 4
    assert square(3) == 9


if __name__ == "__main__":
    main()

""" 밑에 보이는 4줄 코드를 assert를 이용해서 2줄로 줄일 수 있다.
    if square(2) != 4:
        print("2 squared was not 4")
    if square(3) != 9:
        print("3 squared was not 9")
"""

위 코드를 실행하면 터미널에 AssertionError 가 나타나 나를 반긴다.
이 에러는 무엇이며 왜 발생한 것일까?

AssertionError

AssertionError는 Python에서 assert 문이 실패할 때 발생한다.
assert 문은 주어진 조건이 True인지 확인하며, 조건이 False인 경우 AssertionError를 발생시킵니다.

이 AssertionError는 너무 정이 없을 뿐 만 아니라, test_calculator.py파일에 있는 두 개의 assert문 중 어느 부분에서 발생한 에러인지 유저 입장에서는 알 수가 없다. 따라서 user friendly하게 바꿔보자.

from calculator import square


def main():
    test_square()


def test_square():
    try:
        assert square(2) == 4
    except AssetionError:
        print("2 squared was not 4")
    try:
        assert square(3) == 9
    except AssertionError:
        print("3 squared was not 9")


if __name__ == "__main__":
    main()

output: "3 squared was not 9"
이제 어디서 오류가 발생했는지 알 수 있다.

다른 걸 해보자.
일단 우리는 두 개의 파일에 존재하는 함수를 사용하고 있다는 것을 기억하자.
calculator.py 와 test_calculator.py
test_calculator.py에 몇 개의 테스트용 수식을 더 추가해 볼 것이다.

from calculator import square


def main():
    test_square()


def test_square():
    try:
        assert square(2) == 4
    except AssertionError:
        print("2 squared was not 4")
    try:
        assert square(3) == 9
    except AssertionError:
        print("3 squared was not 9")
    try:
        assert square(-2) == 4
    except AssertionError:
        print("-2 squared was not 4")
    try:
        assert square(-3) == 9
    except AssertionError:
        print("-3 squared was not 9")
    try:
        assert square(0) == 0
    except AssertionError:
        print("0 squared was not 0")


if __name__ == "__main__":
    main()

output:
3 squared was not 9
-2 squared was not 4
-3 squared was not 9

0은 0을 더하거나 곱하더라고 0이며
2는 2을 더하거나 곱하더라도 2이다.
따라서 곱셈이 아닌 덧셈으로 calculator.py파일의 계산 식이 바뀌었음에도 오류는 위에 보이는 3개만 출력된다.

그런데 잠깐 멈춰서 생각을 해 보자.
누가 calculator.py에 있는

def square(n):
    return n + n

두 줄의 코드를 점검하기 위해
test_calculator.py처럼

from calculator import square


def main():
    test_square()


def test_square():
    try:
        assert square(2) == 4
    except AssertionError:
        print("2 squared was not 4")
    try:
        assert square(3) == 9
    except AssertionError:
        print("3 squared was not 9")
    try:
        assert square(-2) == 4
    except AssertionError:
        print("-2 squared was not 4")
    try:
        assert square(-3) == 9
    except AssertionError:
        print("-3 squared was not 9")
    try:
        assert square(0) == 0
    except AssertionError:
        print("0 squared was not 0")

if __name__ == "__main__":
    main()

31줄의 코드를 작성하고 싶겠는가?

누구도 test code를 이렇게 작성하고 싶지 않을 것이다.

이 때 등장하는 3rd party labrary

pytest

pip install pytest
[docs.pytest.org]
점검을 자동으로 해준다!!

test_calculator.py 를 눈물을 머금고 새로 작성한다.

from calculator import square

def test_square():
    assert square(2) == 4
    assert square(3) == 9
    assert square(-2) == 4
    assert square(-3) == 9
    assert square(0) == 0

터미널 창을 확대해서 다음 명령어를 실행한다.
"pytest test_calculator.py"

성공과 실패 여부, 위치, 오류값을 모두 관찰할 수 있다.
이제 오류가 발생한 곳인 calculator.py 6번째 줄 code를 수정하여 다시 pytest를 진행해보자.

주의할 점은 다음과 같다.
우리는 test_calculator.py를 점검하는 것이다.
조금 더 정확히 말하면, calculator.py코드에 있는 square 함수를 적용한 test_calculator.py를 점검하는 것이다.
따라서 calculator.py에 존재하는 main()함수는 고려하지 않는다.

곱하기가 더하기로 잘못 적혀진 경우를 점검했다.
그런데 이번 pytest에서 점검 코드가 assert(3)까지 진행되었고 그 다음 오류가 나타나자 그 밑에서 순서를 기다리고 있던 코드들은 실행조차 되지 않았다.
이를 고려하여 코드를 강화시켜보자.

양수, 음수, 0을 따로 점검
홀수, 짝수 따로 점검 등을 할 수 있다.
pytest의 장점은 각 코드들이 자동으로 모두 점검된다는 것이다.
def1, def2, def3가 있는데 모든 코드에서 오류가 발생한다면 각각의 오류를 전부 보여준다는 뜻이다.
그것이 방금 말한 것 처럼 커다란 코드를 일련의 기준으로 분류하여 점검하는 이유이다.

다음은 양수, 음수, 0을 구분하여 점검하는 test_calculator.py코드이다.

from calculator import square

def test_positive():
    assert square(2) == 4
    assert square(3) == 9


def test_negative():
    assert square(-2) == 4
    assert square(-3) == 9


def test_zero():
    assert square(0) == 0

터미널
input: pytest test_calculator.py
output:

양수 점검 시 square(3) 에서 오류 발생
음수 점검 시 square(-2) 에서 벌써 오류 발생
맨 위에 FF는 F가 두 번이라는 뜻.

다시 calculator.py 로 넘어가서 더하기(+)로 잘못 입력된 계산식을 곱하기(*)로 수정한 뒤에 테스트를 진행하자.

기분 좋은 pass! 신호를 볼 수 있다.

테스트를 나눠서 진행하는 법을 배웠다. 기준은 내가 맘대로 정할 수 있다.
양수/정수, 정수/소수, str/int 등등

calculator.py input 절에서 int()를 제거해보자

def main():
    x = input("what's x? ")
    print("x squared is ", square(x))


def square(n):
    return n * n


if __name__ == "__main__":
    main()

터미널에 cat을 입력하면 TypError가 발생한다.

이것도 test해보자!

test_calculator.py 에

import pytest


def test_str():
    with pytest.raises(TypeError):
        square("cat")
        #square() 안에 cat이 들어가면 typerror를 띄우라는 코드인듯

을 추가한다.

터미널을 통해 점검하면 다음과 같은 결과가 나온다.

주어진 코드에는 calculator.pytest_calculator.py 두 파일이 있습니다. 각각의 파일과 그 안의 함수들이 어떻게 동작하는지 자세히 설명하겠습니다.

how unit test process goes

calculator.py

def main():
    x = input("what's x? ")
    print("x squared is ", square(x))


def square(n):
    return n * n


if __name__ == "__main__":
    main()

이 파일은 main 함수와 square 함수를 정의하고 있습니다. 또한, 스크립트가 직접 실행될 때 main 함수를 호출합니다.

1. square 함수

def square(n):
    return n * n
  • 이 함수는 인자로 받은 값을 제곱하여 반환합니다.
  • 입력 값 n이 숫자일 경우 정상적으로 제곱 값을 반환합니다.
  • 문자열이나 다른 타입이 입력될 경우 오류가 발생할 수 있습니다 (현재는 예외 처리가 없습니다).

2. main 함수

def main():
    x = input("what's x? ")
    print("x squared is ", square(int(x)))
  • 사용자의 입력을 받아 이를 int 타입으로 변환합니다.
  • 변환된 값을 square 함수에 전달하여 제곱 값을 계산합니다.
  • 계산된 값을 출력합니다.

3. 직접 실행될 때 main 함수 호출

if __name__ == "__main__":
    main()
  • 이 조건문은 calculator.py가 직접 실행될 때만 main 함수를 호출합니다.
  • 다른 스크립트에서 calculator.py를 모듈로 임포트할 때는 main 함수가 자동으로 실행되지 않습니다.

test_calculator.py

import pytest

from calculator import square

def test_positive():
    assert square(2) == 4
    assert square(3) == 9


def test_negative():
    assert square(-2) == 4
    assert square(-3) == 9


def test_zero():
    assert square(0) == 0


def test_str():
    with pytest.raises(TypeError):
        square("cat")

이 파일은 pytest를 사용하여 calculator.pysquare 함수를 테스트합니다.

1. pytest 임포트와 calculator 모듈에서 square 함수 임포트

import pytest
from calculator import square
  • pytest는 파이썬의 테스트 프레임워크로, 자동화된 테스트를 쉽게 작성하고 실행할 수 있게 해줍니다.
  • calculator 모듈에서 square 함수를 임포트하여 테스트에 사용합니다.

2. test_positive 함수

def test_positive():
    assert square(2) == 4
    assert square(3) == 9
  • square 함수가 양수 입력에 대해 올바른 결과를 반환하는지 테스트합니다.
  • square(2)가 4인지, square(3)가 9인지 확인합니다.

3. test_negative 함수

def test_negative():
    assert square(-2) == 4
    assert square(-3) == 9
  • square 함수가 음수 입력에 대해 올바른 결과를 반환하는지 테스트합니다.
  • square(-2)가 4인지, square(-3)가 9인지 확인합니다.

4. test_zero 함수

def test_zero():
    assert square(0) == 0
  • square 함수가 0 입력에 대해 올바른 결과를 반환하는지 테스트합니다.
  • square(0)가 0인지 확인합니다.

5. test_str 함수

def test_str():
    with pytest.raises(TypeError):
        square("cat")
  • square 함수가 문자열 입력에 대해 TypeError를 발생시키는지 테스트합니다.
  • pytest.raises 문을 사용하여 예외가 발생하는지 확인합니다.
  • 문자열 "cat"을 입력으로 square 함수에 전달할 때 TypeError가 발생하는지 확인합니다.

테스트 실행 절차

  1. pytesttest_calculator.py 파일을 찾고, 그 안의 테스트 함수를 실행합니다.
  2. 각 테스트 함수 (test_positive, test_negative, test_zero, test_str)가 차례로 실행됩니다.
  3. assert 문을 통해 square 함수의 결과가 예상 값과 일치하는지 확인합니다.
  4. test_str 함수에서는 square 함수가 문자열 입력에 대해 TypeError를 올바르게 발생시키는지 확인합니다.
  5. 모든 테스트가 통과하면 pytest는 아무런 오류 메시지 없이 종료됩니다.
  6. 테스트 중 하나라도 실패하면 pytest는 어떤 테스트가 실패했는지와 그 이유를 출력합니다.

이 과정을 통해 square 함수가 다양한 입력 값에 대해 올바르게 동작하는지 검증할 수 있습니다.

unit test의 다른 예시 hello.py VS test_hello.py

hello.py

def main():
    name = input("what's yout name? ")
    hello(name)


def hello(to="world"): #="world" 는 디폴드 값을 world로 설정하는 것임.
    print("hello,", to)


if __name__ == "__main__":
    main()

test_hello.py

from hello import hello


def test_hello():
    assert hello("David") == "hello, David"

위 두 개의 파일을 pytest로 점검하면 pass가 작용하지만 우리의 의도대로 작동하지 않는다.
그 이유는 hello.py파일에 hello()함수의 값이 return되지 않기 때문이다. 따라서 hello.py파일은 다음과 같이 수정되어야 한다.

수정된 hello.py

def main():
    name = input("what's yout name? ")
    print(hello(name))


def hello(to="world"):
    return f"hello, {to}"


if __name__ == "__main__":
    main()

당신이 제공한 두 개의 파일, hello.pytest_hello.py, 그리고 수정된 hello.py 파일은 pytest로 테스트할 때 의도한 대로 동작하도록 하는 과정을 보여줍니다. 여기서 주요 변경 사항과 그 이유를 설명하겠습니다.

원래의 hello.py 파일

def main():
    name = input("what's your name? ")
    hello(name)

def hello(to="world"):
    print("hello,", to)

if __name__ == "__main__":
    main()

원래의 test_hello.py 파일

from hello import hello

def test_hello():
    assert hello("David") == "hello, David"

문제점

  1. 반환 값 없음: 원래의 hello.py 파일에서 hello 함수는 단순히 문자열을 출력하지만, 값을 반환하지 않습니다. print 함수는 None을 반환하기 때문에, test_hello.py에서 hello("David")"hello, David"와 일치하는지 확인하는 assert 문은 항상 실패합니다.
  2. 테스트 실패: assert hello("David") == "hello, David"hello 함수가 문자열 "hello, David"를 반환할 것으로 기대하지만, 실제로는 반환하지 않고 콘솔에 출력만 합니다. 따라서, 반환 값을 테스트할 수 없게 됩니다.

수정된 hello.py 파일

def main():
    name = input("what's your name? ")
    print(hello(name))

def hello(to="world"):
    return f"hello, {to}"

if __name__ == "__main__":
    main()

이유

  1. 반환 값 추가: hello 함수가 문자열을 출력하는 대신 반환하도록 수정되었습니다. 이제 hello 함수는 f"hello, {to}" 문자열을 반환합니다.
  2. 테스트 가능: 이제 hello 함수는 문자열을 반환하므로, test_hello.py에서 assert hello("David") == "hello, David"가 올바르게 작동합니다. hello("David")"hello, David"를 반환하므로, assert 문이 성공합니다.

주요 변경 사항 요약

  • 반환 값을 추가: hello 함수가 문자열을 반환하도록 변경되었습니다. 이는 함수의 출력 값을 테스트할 수 있게 합니다.
  • 출력과 반환 분리: main 함수는 여전히 사용자에게 입력을 받고 hello 함수를 호출하지만, 이제 반환된 값을 print를 통해 출력합니다. 이는 함수의 테스트 가능성을 유지하면서도 사용자에게 출력할 수 있게 합니다.

최종 코드 설명

수정된 hello.py 파일:

def main():
    name = input("what's your name? ")
    print(hello(name))

def hello(to="world"):
    return f"hello, {to}"

if __name__ == "__main__":
    main()
  • main 함수는 사용자로부터 이름을 입력받고, hello 함수를 호출하여 반환된 값을 출력합니다.
  • hello 함수는 입력된 이름을 받아 "hello, {to}" 형식의 문자열을 반환합니다. 기본 값은 "world"입니다.
  • 이로 인해 hello 함수는 테스트 가능해지고, 실제 코드에서도 원하는 출력을 생성합니다.

수정된 test_hello.py 파일:

from hello import hello

def test_hello():
    assert hello("David") == "hello, David"
  • test_hello 함수는 hello 함수가 "David"를 입력받았을 때 "hello, David"를 반환하는지 확인합니다.

이렇게 변경함으로써 hello.py 파일은 기능적으로 의도한 대로 동작하며, test_hello.py 파일은 pytest를 통해 올바르게 작동하는지 검증할 수 있습니다.

앞에 calculator 예시에서 처럼 test 함수를 나눠보자
test_hello.py

from hello import hello


def test_hello():
    assert hello() == "hello, world"


def test_argument():
    for name in ["Hermione", "Harry", "Ron"]:
            assert hello(name) == f"hello, {name}"

pytest 결과
업로드중..

2개의 댓글

comment-user-thumbnail
2024년 5월 1일

Welcome to Velog, Bro : )

1개의 답글