미나랑 해보자

220113 python 네번째 수업: 함수 입출력(사용자정의함수, 입출력, 파일 읽고 쓰기) 본문

미나랑의 고군분투 공부일지/기초 python 연습

220113 python 네번째 수업: 함수 입출력(사용자정의함수, 입출력, 파일 읽고 쓰기)

미나랑 2022. 1. 13. 21:21

결국 마크다운을 그대로 올려 쉽게 가겠다는 편한생각은 버리기로 결정했다.

조금 번거롭더라도 깔끔하게 코드블럭으로 올리기로..!

네번째 시간에는 사용자정의 함수, 입력(input())과 출력(print), 그리고 파일 읽고 쓰는 방법에 대해 공부했다.

 

- 사용자 정의 함수

함수를 왜 만들어야 하나?

사용자정의함수의 기본구조: 기본형태, 입력값이 없는 경우, 결괏값이 없는 경우, 입력값 결괏값 모두 없는 경우

매개변수 지정해주기

입력값을 여러 개 받아줄 때

속성: 결괏값은 하나만 발생한다.

return기능 활용하기: 특정 상황에서 빠져나가기

함수의 초기값

전역변수와 지역변수

lambda

 

- 입출력

입력(input)

출력(print)

 

- 파일 읽고 쓰기

파일 쓰기

파일 내용 넣기

파일 읽기: readline, readlines, read

1. 사용자정의함수

파이썬 내에 있는 함수를 그대로 사용하는 것이 아닌 사용자가 스스로 함수를 정의해 활용하는 것

1) 함수를 왜 만들어야 하나?

반복적으로 어떤 함수를 사용해야할 때 매번 입력하는 것은 효율적이지 못함. 직접 만들면 흐름이 유연해지고 오류를 발견하기 수월함   

2) 사용자정의함수의 기본 구조

(syntax)   
def 함수의 이름(파라미터):   
    수행할 문장   
    return 결괏값   
** def는 함수를 만드는 예약어   
** 파라미터: 함수에서 입력으로 전달받는 매개변수

 

(1) 기본적인 사용자 정의함수의 형태

def add(a, b):
    result = a + b
    return result

result 변수에 넣지 않고도 return 에서 바로 반환이 가능하다.

def add(a, b):
    return a + b
# 만든 함수의 활용
i = 3
j = 4
c = add(i, j)
print(c)
7

*** 파라미터와 인수

- 파라미터(매개변수): 함수의 입력으로 전달된 값을 받는 변수. 입력받을 때의 변수
- 인수(argument): 함수를 호출할 때(사용할 때) 전달하는 입력값.

둘 다 입력값이지만 이러한 차이가 있다.

## 오류를 내 맘껏 발생시켜보자
add(3) #argument 'b'
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
C:\Users\MINALA~1.DES\AppData\Local\Temp/ipykernel_8576/3851484574.py in <module>
      4 ## 오류를 내 맘껏 발생시켜보자
      5 
----> 6 add(3)

TypeError: add() missing 1 required positional argument: 'b'

Q: 왜 에러가 발생했는가? 

A: 사용자가 정의한 함수 add()는 파라미터가 a, b 두 개인데 하나만 입력했기 때문에 a+b의 값을 출력할 수 없기 때문이다. 여기서는 출력해야하는 인수(argument) 'b'가 없다고 메세지가 뜬다.

 

(2) 입력값이 없는 함수

#입력값이 없는 함수
def say(): #파라미터가 없어요
    return 'Hi'
say()
'Hi'

입력값이 없지만 결괏값으로 'Hi'를 출력한다.

 

(3) 결괏값이 없는 함수

입력값이 없는 함수도 있지만 결괏값(return)이 없는 사용자정의함수도 만들 수 있다.

#결괏값이 없는 함수
def add(a, b):
    print("%d, %d의 합은 %d입니다." %(a, b, a +b))
add(5, 6)
5, 6의 합은 11입니다.

 

변수에 넣었을 때 그 차이를 확인할 수 있다.

c = add(5, 6)
5, 6의 합은 11입니다.
print(c)
None

Q: 왜 변수 c의 값이 None일까?

함수 add()는 단순히 값을 출력하는 기능을 수행할 뿐 결괏값이 저장되지는 않는다. 

따라서 결괏값이 None이 나오고 이는 비어있는 상태이다.

 

(4) 입력값도 결괏값도 없는 함수

둘 다 없을 수도 있다.

def say():
    print('Hi')
say()
Hi

3) 매개변수 지정해주기

#매개변수 지정
def add(a, b):
    return a + b
# a에 3을, b에 7을 넣어주기(매개변수 지정)
add(a = 3, b = 7)
10
# 더하기는 이런게 관계없을 수 있지만 많은 경우(예: 빼기)에 파라미터 지정해주는게 좋다
def minus(a, b):
    return a - b
print(minus(7, 3))
print(minus(3, 7))
4
-4

매개변수 지정해주면 같은 결괏값을 갖도록 만들어줄 수 있다.

#따라서
print(minus(a = 7, b = 3))
print(minus(b = 3, a = 7))
4
4

4) 입력값을 여러 개 받아줄 때

# 입력값이 몇 개가 될 지 모를 때(여러개 받아줄 때)
# 예: 들어온 변수들을 다 더해주는 함수
#파라미터에 *붙이면 입력값을 모두모아 튜플로 받아준다
def add_many(*args):
    result = 0
    for i in args:
        result += i
    return result
result = add_many(1, 2, 3, 4)
print(result)
10

예2)

#choice에 따라 합 혹은 곱을 연산하는 함수
def add_mul(choice, *args): #파라미터에 *만 붙여주면 아무문자나 관계없음
    if choice == 'add':
        result = 0
        for i in args:
            result += i
    elif choice == 'mul':
        result = 1
        for i in args:
            result *= i
    return result
a = add_mul('add', 1, 2, 3, 4, 5, 6)
print(a)
b = add_mul('mul', 1, 2, 3, 4, 5)
print(b)
21
120

5) 속성: 결괏값은 언제나 한개만 발생한다

# 결괏값은 언제나 한개만 발생한다.
def add_and_mul(a, b):
    return a + b, a * b
result = add_and_mul(3, 4)
print(result) #결괏값이 튜플 하나로 묶여 나온다.
(7, 12)

출력값이 하나의 튜플로 묶여 나오는 것을 확인할 수 있다.

이러한 현상을 해결하기 위해서는 반환되는 각 값을 받아줄 수 있는 변수를 개별적으로 설정하면 된다.

# 각각 출력되면 좋겠어
a, b = add_and_mul(7, 10)
print(a)
print(b)
17
70

그렇다면, 함수 내에서 결괏값을 두 개 만들 수는 없을까?

시도해보기

# 함수에선 어떻게 안되나?
def add_and_mul(a, b):
    return a + b 
    return a * b
result = add_and_mul(2, 3)
print(result) 
# return 두개로 하면 맨 앞 값만 return된다.
# def에서 return문은 return문 만나면 구문이 끝남. 따라서 제일 마지막에 와야함
#마지막이므로 가장 첫번째 입력된 return만 실행됨
5

사용자정의함수에서 return문이 실행되면 구문이 끝나기 때문에 가장 첫번째 입력된 return문만 수행하고 함수가 끝나게 된다.

6) return 기능 활용하기: 특정한 상황에서 빠져나가기

# 특별한 상황일 때 빠져나가기
def say_a(a):
    if a == 'a':
        return
    print("나는 'a'가 아닙니다. %s입니다." % a) 
    #a가 입력된다면 print문 구동안되고 빠져나감
print(say_a('a')) #결괏값이 없으므로 None
None

**** 이 부분이 중요하다***

print(say_a('b')) #일단 print문이 실행이 됨. 
#print를 했어서 결과값까지 출력된거 이 구문에서는 return값이 none인게 맞음
나는 'a'가 아닙니다. b입니다.
None

"출력"을 실행했기 때문에 함수에서 지정된 print구문 뿐만 아니라 함수의 결괏값도 함께 출력된다.

따라서 a가 아닌 문자를 입력했을 때 실행되는 나는 'a'가 아닙니다. %s입니다. 뿐만 아니라 결괏값인 None도 함께 출력되는 것이다.

cf. 그냥 실행만 하면 어떻게 될까?

say_a('b')
나는 'a'가 아닙니다. b입니다.

print없이 함수만 실행시키면 실행문장만 출력되는 것을 확인할 수 있다.

7) 함수의 초기값

초기값을 미리 설정해줄 수 있다. 매개변수 woman 의 경우 True가 초기값이다.

#함수의 기본값이 디폴트값
def say_myself(name, age, woman = True):
    print('나의 이름은 %s입니다.' % name)
    print('나는 %s살 입니다.' % age)
    if woman:
        print('여자입니다.')
    else: 
        print('남자입니다')
say_myself('aaa', 20, woman= True)
나의 이름은 aaa입니다.
나는 20살 입니다.
여자입니다.

초기값 설정 시 주의사향: 디폴트 값을 설정해준 변수는 맨 뒤로 가야한다. 안그러면 에러생김

만약, 초기값을 가진 변수가 두 개 이상이라면? 둘 다 뒤에 적어주면 된다.

예)

## 초기값 설정시 주의사항
#함수의 기본값이 디폴트값
def say_myself(name, woman = True, age = 20): #디폴트가 필요한 변수는 뒤에 가야함
    print('나의 이름은 %s입니다.' % name)
    print('나는 %s살 입니다.' % age)
    if woman:
        print('여자입니다.')
    else: 
        print('남자입니다')
say_myself('aaa', age = 24)
나의 이름은 aaa입니다.
나는 24살 입니다.
여자입니다.

8) 전역변수와 지역변수

# 매개변수의 효력
a = 1
def vtest(a):
    a += 1
    
vtest(a)
print(a)
# 함수 안에서만 매개변수는 효력이 있다.
#함수를 실행시켜서 함수 밖 변수에게 영향주고 싶으면?
1

매개변수는 함수 안에서만 효력을 가진다. 따라서, 함수 밖에서는 해당 변수와 이름이 같더라도 영향을 주지 못한다.

위의 경우 함수 vtest의 매개변수 a는 함수 밖 변수와 이름이 같지만 함수 내에서만 효력을 가지기 때문에 a를 출력하면 1이 나온다.

a = 1
def vtest(a):
    a += 1
    return a
    
vtest(a)
print(a)
1

return을 사용하더라도 결과는 마찬가지이다.

a = 1
def vtest(a):
    a += 1
    return a
    
print(vtest(a))
print(a)
# 함수 실행했을 때는 변화있지만 a그자체 변하지 않았다
2
1

위의 경우에서 볼 수 있듯이 함수를 실행한 결과 print(vtest(a))는 변화가 있지만 이것이 변수 a자체에 영향을 주지는 못하였다. 독립적이라는 뜻이다.

 

그렇다면, 함수 안의 변수와 함수 밖의 변수를 연결하는 방법은 무엇일까?

변수 a를 전역변수로 설정해주는 것이다. 

a = 1
def vartest():
    global a 
    #함수 밖에 있는 a 변수를 직접 가져와서 그 변수를 직접 변화시키겠다.
    #밖에 있는 변수를 변화하기 때문에 주의해서 사용해야함
    #코딩 잘하는 사람들은 잘 안씀. 함수는 독립적으로 사용해야하기 때문.    
    a += 1
vartest()
print(a)

함수 안 지역변수 a를 전역변수(global)로 설정함으로써 함수 밖 변수를 직접 가져와 사용하겠다고 선언할 수 있다. 그러나 이는 밖에 있는 변수를 변화하기 때문에 주의해서 사용해야하고 에러가 생겼을 때 복잡해질 수 있기 때문에 자주사용하는 방법은 아니다.

보통은 변수 a를 한번 더 설정하는 것으로 해결한다.

# 해결
a = 1
def vtest(a):
    a += 1
    return a
    
a = vtest(a)
print(a)
2

9) lambda

def add(a, b):
    return a + b #람다를 사용하지 않은 형태
add = lambda a, b: a + b
# syntax: lambda 매개변수 : 얻고 싶은 결과물
# 뒷부분에 또 나오니까 이런 형식이다를 기억해두자
print(add(3, 4))
7

 

2. 입력과 출력

1) 입력: input()

#입력받기 input()
a = input() #입력창 뜨는게 프롬프트라고 함

프롬프트에서 원하는 입력값을 타이핑해 넣을 수 있다.

a # 내가 입력한게 문자열로 할당이 됨

출력값: 입력한 문자열

프롬프트에 어떤 값을 입력해야하는지 알려주고 싶을 때

num = input('숫자를 입력하세요: ') #질문내용을 입력하면 됨
숫자를 입력하세요: 3
num
3

2) 출력: print()

#print문 복습하기
print('a' 'b' 'c') # 문자열일 경우에 그냥 나열할 수 있다.
print('a'+'b'+'c') # +로 연결하는 것과 같음
print('a', 'b', 'c') # 기본 seperator가 공백이다
abc
abc
a b c

단 숫자는 print(1 2 3)의 형태로 입력할 수 없음. +기호를 넣으면 더하기로 인식해서 합이 출력된다.

(1) sep

print('a', 'b', 'c')의 경우 구분자(sep)의 디폴트인 공백을 공백없음으로 설정해주면 위의 경우와 똑같은 결과가 나온다.

# sep사용
print('a', 'b', 'c', sep='') 
# 기본 seperator가 공백인데 공백지워주면 앞의 두 경우와 마찬가지로 abc로 출력된다.
abc

(2) end

출력값은 디폴트로 줄바꿈을 하게 되어있다.

for i in range(10):
    print(i) #줄바꿈되어 출력됨
0
1
2
3
4
5
6
7
8
9

하지만, 값을 설정해주는 것에 따라 다른 형태로도 출력이 가능하다

for i in range(10):
    print(i, end = ' ') #띄어써서 출력됨
0 1 2 3 4 5 6 7 8 9

3. 파일 읽고 쓰기

1) 파일 쓰기

#파일 생성하기
f = open("새파일.txt", 'w') #파일이름, 모드(쓰기=w, 읽기=r)
f.close() #파일 닫기

2) 파일 쓰기(내용 넣기)

f = open("새파일.txt", 'w')
for i in range(1, 11):
    f.write("%d번째 줄입니다.\n" % i)
f.close()

3) 파일 읽어오기

(1) readline(한 줄만 읽어올 때)

#파일 읽어오기(readline, readlines, read)
f = open("새파일.txt", 'r')
line = f.readline() #한 줄을 읽어오겠다(readline)
print(line)
f.close()
1번째 줄입니다.

한 줄 씩 읽어오기

#파일 읽어오기(readline, readlines, read)
f = open("새파일.txt", 'r')
line = f.readline() #한 줄을 읽어오겠다(readline)--> 첫번째 줄
print(line)
line = f.readline() #한 줄을 읽어오겠다(readline)--> 두번째 줄
print(line)
line = f.readline() #한 줄을 읽어오겠다(readline)--> 세번째 줄
print(line)
f.close()
1번째 줄입니다.

2번째 줄입니다.

3번째 줄입니다.

while문을 활용하여 모든 줄을 다 읽어올 수 있다.

#while문을 활용하여 다 읽어오기
f = open("새파일.txt", 'r')
while True:
    line = f.readline() #한 줄을 읽어오겠다(readline)
    if not line: break #line이 없으면 루프를 멈추겠다.
    print(line)
f.close()
1번째 줄입니다.

2번째 줄입니다.

3번째 줄입니다.

4번째 줄입니다.

5번째 줄입니다.

6번째 줄입니다.

7번째 줄입니다.

8번째 줄입니다.

9번째 줄입니다.

10번째 줄입니다.

변수에 저장하는 게 아니라 단순히 출력만 하는 것이다.

 

(2) readlines(모든 줄을 리스트형태로 읽어오기)

#readlines
f = open("새파일.txt", 'r')
lines = f.readlines() #한 줄을 읽어오겠다(readline)
print(lines)
f.close() 
#리스트 형태로 출력. 각 줄이 하나의 요소로 = 10개의 요소
['1번째 줄입니다.\n', '2번째 줄입니다.\n', '3번째 줄입니다.\n', '4번째 줄입니다.\n', '5번째 줄입니다.\n', '6번째 줄입니다.\n', '7번째 줄입니다.\n', '8번째 줄입니다.\n', '9번째 줄입니다.\n', '10번째 줄입니다.\n']

data type이 list이다.

print(type(lines))
len(lines)
<class 'list'>
10

(3) read(파일 전체를 문자열로 읽어오기)

#read
f = open("새파일.txt", 'r')
data = f.read() #모두 읽어오겠다(read)
print(data)
f.close() 
#전체내용을 하나의 문자열로 읽어온다.
1번째 줄입니다.
2번째 줄입니다.
3번째 줄입니다.
4번째 줄입니다.
5번째 줄입니다.
6번째 줄입니다.
7번째 줄입니다.
8번째 줄입니다.
9번째 줄입니다.
10번째 줄입니다.
type(data)
str

Q: 파일을 닫는 걸 늘 까먹을 것 같다면?

with open()을 사용하자

with open("새파일.txt", 'r') as f:
    data = f.read()
print(data)
#파일을 닫는 코드 없이 파일을 불러오고, 쓸 수 있다.
1번째 줄입니다.
2번째 줄입니다.
3번째 줄입니다.
4번째 줄입니다.
5번째 줄입니다.
6번째 줄입니다.
7번째 줄입니다.
8번째 줄입니다.
9번째 줄입니다.
10번째 줄입니다.