파일 입출력
파이썬에서도 파일을 열어서 수정하는 것이 가능하다. 먼저 open 함수로 파일을 열고, write 함수를 이용해 파일을 수정하면 된다. 파일 수정을 마쳤다면 close 함수를 이용해 파일을 닫아주어야 한다.
open(file_name, file_mode)
열고자 하는 파일의 이름을 첫 번째 인자로, 해당 파일의 모드를 두 번째 인자로 넘겨준다. 만약 파일을 읽기만 할 거라면 'r', 파일을 쓰고자(수정하고자)한다면 'w' 모드로 실행하면 된다.
write('내용')
파일에 내용을 쓰고자 한다면 write 함수를 이용해서 추가해주면 된다. 이 때, 쓰고자 하는 파일에 대해 write 함수를 실행해야 하기 때문에
만약 파일 객체의 이름을 file로 만들었다면 file.write('내용')와 같은 형태로 사용해야 한다.
close()
해당 파일을 닫는 함수이다. 마찬가지로 닫고자 하는 파일에 대해 file.close()의 형태로 사용해야 한다.
#hello.py
file = open('file.txt', 'w')
file.write('Hello, world!')
file.close()
$ python hello.py
$ ls
file.txt hello.py
$ cat file.txt
Hello, world!
read()
파일의 내용을 읽어오고 싶다면 read 함수를 사용하면 된다. read() 함수는 파일의 내용을 문자열 형태로 반환하기 때문에, 해당 내용을 저장하는 변수의 타입은 string이 된다. 파일을 읽어오고자 한다면 파일 객체를 만들 때, open 함수의 모드를 읽기 모드('r')로 만들어주어야 한다.
#hello.py
file = open("file.txt", 'r')
s = file.read()
print(s)
file.close()
#file.txt
Hello, world!
$ python hello.py
Hello, world!
with ~ as
열고, 읽거나 쓰고, 닫는 것이 파일 입출력의 기본 형태이다. 파일을 여는 open 함수와 닫는 close 함수는 항상 세트로 붙어다니는데, 이 둘을 하나로 합쳐놓은 기능을 하는 함수가 바로 with ~ as이다. 사용법은 아래와 같다.
with open(file_name, mode) as file:
내용
with as는 사용하면 파일 사용이 끝나면 자동으로 파일을 닫아준다. 사용법은 위의 file 객체를 생성해서 사용하는 것과 동일하다.
writelines(list)
리스트에 들어있는 문자열을 파일에 쓰고자 할 땐 writelines 함수를 이용하면 된다. 이 때, 리스트의 내용들이 한 줄에 파일에 저장되기 때문에 여러줄로 저장하고자 한다면, 리스트의 각 요소들의 끝에 '\n'을 꼭 써주어야 한다.
#ListToFile.py
a = ['nice \n', 'to \n', 'meet \n', 'you \n']
with open('file.txt', 'w') as file:
file.writelines(a)
$ python ListToFile.py
$ cat file.txt
nice
to
meet
you
readlines(list)
반대로 파일의 내용을 리스트에 저장할 땐 readlines 함수를 사용하면 된다.
#FileToList.py
with open('file.txt', 'r') as file:
fileList = file.readlines()
print(fileList)
$ python FileToList.py
['nice \n', 'to \n', 'meet \n', 'you \n']
pickle 모듈
위의 입출력은 모두 문자열에 대한 것이지만, 파일엔 문자열 뿐만 아니라 객체들도 저장할 수 있다. 이 때에는 피클(pickle)이라는 모듈을 사용해야 한다. 해당 객체를 파일에 저장하는 과정을 피클링(pickling), 파일에 저장된 값을 개체로 불러오는 것을 언피클링(unpickling)이라 한다. 객체를 파일로 저장할 때에는 피클 모듈에 정의되어있는 dump 함수를 이용한다.
#Pickle.py
import pickle
name = 'james'
age = 17
address = '서울시 서초구 반포동'
scores = {'korean': 90, 'english': 95, 'mathematics': 85, 'science': 82}
with open('james.p', 'wb') as file: # james.p 파일을 바이너리 쓰기 모드(wb)로 열기
pickle.dump(name, file)
pickle.dump(age, file)
pickle.dump(address, file)
pickle.dump(scores, file)
$ python Pickle.py
$ ls
Pickle.py james.p
pickle.dump를 이용해서 객체를 저장하는 경우엔 파일모드를 꼭 'wb'로 지정해주어야 한다. b는 바이너리(binary)의 앞 글자를 따온 것으로, 컴퓨터는 0과 1로만 처리할 수 있기 때문에 바이너리로 저장해주어야 한다. Pickle.py를 실행하면 james.p 라는 파일이 생성된다. 이러한 과정이 피클링이다.
파일로 저장된 객체를 읽어오기 위해서는 pickle 모듈 내의 load 함수를 이용해야 한다.
#UnPickle.py
import pickle
with open('james.p', 'rb') as file: # james.p 파일을 바이너리 읽기 모드(rb)로 열기
name = pickle.load(file)
age = pickle.load(file)
address = pickle.load(file)
scores = pickle.load(file)
print(name)
print(age)
print(address)
print(scores)
$ python UnPickle.py
james
17
서울시 서초구 반포동
{'korean': 90, 'english': 95, 'mathematics': 85, 'science': 82}
james.p에 저장된 값들을 불러와서 변수에 저장한 후에 변수들을 출력하면 정상적으로 출력되는 것을 볼 수 있다.
함수
코드를 작성하다 보면, 동일한 동작을 값만 바꾸어서 여러 번 사용해야 하는 경우가 있다. 그 때마다 해당 코드를 작성하면 코드가 길어지고 가독성이 떨어지게 되는데, 같은 동작을 여러 번 반복해서 사용해야 하는 경우 함수로 작성해서 해당 함수를 호출하는 식으로 재사용성과 가독성을 높일 수 있다. 함수를 정의하는 기본 형태는 아래와 같다.
def 함수이름():
내용
Hello, world!를 출력하는 동작을 함수로 작성하고 사용하는 방법은 아래와 같다.
#func.py
def hello():
print('Hello, world!')
hello()
$ python func.py
Hello, world!
함수를 사용할 때 주의할 점은, 함수의 호출이 함수가 정의된 부분보다 위에 있으면 안된다는 것이다. 만약 함수를 정의하지 않은 채 해당 함수를 호출하려하면 에러가 발생한다.
hello 함수는 매개변수도 없고, 반환 값도 없는 함수이다. 만약, 어떠한 값을 매개변수로 받아서 해당 값에 5를 더한 값을 반환하고자 한다면, 아래와 같이 작성하면 된다.
#plus5.py
def plus5(x):
return x+5
a = int(input())
print(plus5(a))
$ python plus5.py
7
12
매개변수와 반환 값은 여러 개가 될 수 있으며, 각각의 매개변수와 반환 값들을 콤마(,)로 구분지어주면 된다.
리스트 언패킹
함수를 정의할 때, 매개변수를 하나만 정의하면 해당 함수를 사용할 때에도 한 개의 매개변수만 넘길 수 있다. 하지만 상황에 따라 정해놓은 갯수보다 많이 매개변수를 넘겨주어야 할 경우가 생긴다. 이를 가능하게 해주는 기능이 바로 패킹과 언패킹이다. 쉽게 설명해서, 매개변수를 하나만 받는 함수에 여러 개의 값을 하나로 묶어서 하나의 매개변수처럼 넘겨주고, 해당 함수에서는 이를 각각의 변수로 풀어서 여러 개의 변수를 사용하는 것이다.
리스트 또는 튜플을 예로 들 수 있는데, 리스트에 [1, 2, 3, 4, 5]라는 값이 존재한다고 가정한다. 그리고 매개변수로 넘겨받은 변수를 출력해주는 함수인 print_var(x) 함수가 존재한다고 가정한다. 이 때, a라는 리스트를 만들어 [1, 2, 3, 4, 5]를 넣어주고, 이를 print_var의 매개변수로 넘겨주면 a라는 리스트 변수 하나를 매개변수로 넘긴 것이지만, 실제로 1, 2, 3, 4, 5 다섯 개를 넘겨준 것처럼 사용할 수 있다. 이 때, 패킹 된 변수를 매개변수로 넘겨줄 땐 변수의 이름 앞에 애스터리스크(*)을 붙여주어야 한다.
#def.py
def print_var(x):
print(x)
a = [1, 2, 3, 4, 5]
print(*a)
$ python def.py
1 2 3 4 5
함수를 정의할 때 이를 가변 인수 함수로 만들어 주는 방법도 있다. 함수를 정의할 때 매개변수 정의 부분에 *매개변수 로 정의해주면 된다.
def print_var(*x):
for i in x:
print(i)
print_var(10)
print_var(10, 20)
print_var(10, 'Hello')
$ python def.py
10
10
20
10
Hello
람다 표현식
함수를 작성할 때 def을 이용하는 것 말고도, 람다 표현식을 이용하는 방법도 있다. 람다 표현식은 식의 형태로 되어 있다고 하여 람다 표현식이라 하고, 비교적 함수를 간편하게 작성할 수 있기 때문에 보통은 다른 함수의 매개변수로 넣을 때 자주 사용한다. 또한 람다 표현식이 만드는 함수는 이름이 없기 때문에 익명 함수라고 부르기도 한다. 사용법은 다음과 같다.
lambda x: x + 10
x를 매개변수로 받아서 x + 10을 반환해주는 함수이다. 하지만 람다 표현식은 함수 이름이 없기 때문에 이를 호출해서 사용할 수는 없고, 보통은 변수 등에 할당해주는 식으로 쓰인다.
>>> plus_ten = lambda x: x + 10
>>> plus_ten(1)
11
람다 표현식 활용
람다 표현식과 map, filter, reduce 등의 함수를 함께 사용할 수 있다.
예를들어, 1부터 10까지 저장되어 있는 a라는 리스트에서, 3의 배수만 문자열로 바꿔주고자 한다면 람다 표현식과 map 함수를 이용해 한줄에 처리해 줄 수 있다.
a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
b = list(map(lambda x: str(x) if x%3==0 else x, a))
print(b)
if문을 이용해서 x가 3의 배수이면 문자열 형태로 바꾸어 반환하고, 3의 배수가 아니면 그대로 반환한다. 람다 표현식 내에서 if를 사용했다면, 반드시 else도 사용해주어야 한다.
전역 변수
파이썬에도 전역 변수의 개념이 존재한다. 다음은 전역 변수 x를 함수 내에서 출력하는 코드이다.
x = 10
def foo():
print(x)
foo()
print(x)
$ python def.py
10
10
전역 변수 x를 foo 함수 내부에서 사용하여 출력하였다. 그렇다면 함수 내에서 전역 변수 x의 값을 바꾸면 어떻게 될까?
x = 10
def foo():
x=20
print(x)
foo()
print(x)
$ python def.py
20
10
foo함수를 통해 출력한 x는 20이 나왔지만 이후에 print(x)를 통해 x를 출력하면 10이 나온다. 얼핏 보기엔 foo 함수내에서 전역변수 x의 값을 바꾸어준 것처럼 보이지만, 실제로는 foo 함수에서 선언한 지역변수이다. 해당 함수 내에서 전역 변수 x에 대해 접근하기 위해선 global 키워드를 이용해야 한다.
x = 10
def foo():
global x
x = 20
print(x)
foo()
print(x)
$ python def.py
20
20
위와 같이 global x를 통해 전역 변수 x를 사용하겠다고 선언한 뒤에 사용해주어야 한다.
클래스
파이썬, C++, 자바 등의 언어를 객체지향 프로그래밍이라 한다. 객체란, 변수들과 그와 관련된 메소드들이 모여서 이루어진 하나의 꾸러미라고 볼 수 있다. 예를 들어, TV라는 사물이 볼륨버튼, 음량버튼, 전원버튼으로 이루어져있다고 가정한다. 이 때, 각각의 버튼들이 변수들이 될 것이고, 볼륨 + 버튼을 눌러서 발생하는 동작(소리가 커지는)을 메소드라고 한다. 이러한 변수와 메소드로 이루어진 TV를 하나의 객체라고 표현한다.
클래스를 생성하는 방법은 아래와 같다.
class 클래스이름:
def(self):
내용
클래스를 생성할 때 몇 가지 규칙이 있는데, 클래스이름은 보통 대문자로 시작한다. 또, 클래스 내에서 선언한 메소드의 첫 번째 매개변수는 반드시 self여야 한다. 아래는 클래스의 사용 예시이다.
class Person:
def greeting(self):
print('Hello')
Person이라는 클래스를 만들어 그 안에 인사를 하는 메소드 greeting을 정의하였다. 이를 사용하는 방법은 다음과 같다.
>>> james = Person()
>>> james.greeting()
Hello
클래스의 메소드를 사용하고자 한다면, 인스턴스를 통해 호출해야 하는데, 이런 메소드를 인스턴스 메소드라고 한다.
속성
클래스 내에서 속성을 만들 때는 __init__ 메소드를 선언하고, 해당 메소드 내에서 self.속성 = 값 을 통해 속성에 값을 할당할 수 있다.
class Person:
def __init__(self):
self.hello = '안녕하세요.'
def greeting(self):
print(self.hello)
james = Person()
james.greeting()
코드를 해석하자면, 먼저 Person이라는 클래스를 정의한다. 해당 클래스 내에서 __init__ 메소드를 정의하고, hello 라는 속성에 '안녕하세요.'라는 값을 저장한다. 그 후에 dreeting이라는 메소드를 정의하고, 해당 클래스의 속성인 hello를 출력하도록 내용을 작성한다.
이후에 james라는 변수에 Person 클래스를 할당한 후에, james 인스턴스를 통해 greeting 메소드를 호출해서 '안녕하세요'를 출력하게 된다.
비공개 속성
만약, 해당 속성을 클래스 내에서만 사용하고 클래스 외부에서 인스턴스를 통해 접근하는 것을 막고자 한다면 속성을 비공개로 만들 수 있다. 비공개 속성은 속성의 이름 앞에 언더바(_)를 두 개 붙여서 __속성 으로 선언해야 한다.
class Person:
def __init__(self, name, age, address, wallet):
self.name = name
self.age = age
self.address = address
self.__wallet = wallet # 변수 앞에 __를 붙여서 비공개 속성으로 만듦
maria = Person('마리아', 20, '서울시 서초구 반포동', 10000)
maria.__wallet -= 10000 # 클래스 바깥에서 비공개 속성에 접근하면 에러가 발생함
해당 코드는 한 사람의 이름, 나이, 주소, 지갑을 클래스로 만들어서 maria라는 변수에 할당하는 코드이다. 이 때, 지갑의 내용은 외부에서 함부로 바꾸면 안되기 때문에, wallet 앞에 언더바 두 개를 사용해서 __wallet으로 속성을 만들었다. 해당 코드를 실행하면 맨 마지막줄 maria.__wallet -= 10000에서 에러가 발생하는 것을 볼 수 있다.
비공개 속성을 만들었다면, 해당 속성을 이용할 수 있는 메소드를 클래스 내에 정의해주어서, 해당 속성에 접근하고자 할때 메소드를 이용해서 접근하도록 해주어야 한다.
클래스 속성
__init__ 메소드에서 만드는 속성은 인스턴스 속성이라 한다. 이러한 방법 말고 클래스에 바로 속성을 만드는 방법도 있는데, 이를 클래스 속성이라 한다. 클래스 속성은 클래스 내에서 변수를 바로 선언해주면 된다.
class Person:
bag = []
def put_bag(self, stuff):
self.bag.append(stuff)
james = Person()
james.put_bag('책')
maria = Person()
maria.put_bag('열쇠')
print(james.bag)
print(maria.bag)
Person 클래스에 bag이라는 리스트를 만들고, put_bag 메소드를 선언해서 bag 리스트에 값을 추가하는 기능을 정의하였다.
이 때, 클래스를 저장한 변수 james와 maria에 각각 책, 열쇠를 저장하는데, 실제로 james.bag과 maria.bag을 출력하면 둘 다 ['책', '열쇠']가 출력된다. 이를 통해, 클래스 변수는 모든 인스턴스가 공유해서 쓴다는 것을 알 수 있다. bag 리스트를 공유하지 않고 사용하고 싶다면 __init__ 메소드 내에서 인스턴스 속성으로 선언해주면 된다.
정적 메소드
클래스 내의 메소드를 사용할 때, 보통은 인스턴스를 이용해서 인스턴스.메소드 의 형태로 사용했지만, 정적 메소드를 이용해서 인스턴스를 이용하지 않고 메소드를 바로 호출 할 수 있다. 정적 메소드를 선언할 땐 그 위에 @staticmethod를 붙여주어야 한다. 아래는 Calc 라는 클래스 내에 덧셈 기능인 add 메소드를 정적으로 선언하여 사용하는 예시이다.
class Calc:
@staticmethod
def add(x, y):
print(x+y)
Calc.add(3, 4)
Calc.add(5, 6)
$ python Calc.py
7
11
정적 메소드는 self 매개변수를 받지 않기 때문에 인스턴스 속성엔 접근할 수 없다. 그렇기 때문에 보통은 인스턴스 속성, 인스턴스 메소드가 필요 없을 때 정적 메소드로 사용한다.
클래스 메소드
클래스 메소드는 메소드 위에 @classmethod를 붙여서 사용하며, 클래스 메소드의 첫 번째 매개변수는 cls로 지정해주어야 한다. cls는 현재의 클래스가 저장되어 있기 때문에, cls.속성 을 이용해 해당 클래스 내의 속성에 접근할 수 있다.
class Person:
count = 0 # 클래스 속성
def __init__(self):
Person.count += 1 # 인스턴스가 만들어질 때
# 클래스 속성 count에 1을 더함
@classmethod
def print_count(cls):
print('{0}명 생성되었습니다.'.format(cls.count)) # cls로 클래스 속성에 접근
james = Person()
maria = Person()
Person.print_count() # 2명 생성되었습니다.
james, maria에 Person 클래스를 저장하기 때문에 count는 2가 된다. 마지막 줄의 Person.print_count는 인스턴스 없이 바로 메소드를 호출했는데, print_count는 해당 클래스 내의 count 속성을 사용해야 하기 때문에 클래스메소드로 선언하였다. 이 때 count 속성에 접근하기 위해 cls.count로 사용하였다.
상속
파이썬의 클래스에는 상속이라는 개념이 존재한다. 상속이란 무언가를 물려준다는 의미인데, 클래스에서의 상속도 같은 의미로 사용된다. 어떠한 클래스 A가 존재하고, A 클래스의 기능을 물려받은 채 다른 기능을 추가한 클래스를 B 클래스라고 하면, 기능을 물려주는 A클래스를 기반 클래스(Base Class), 기능을 물려받아(상속받아) 새롭게 만든 B 클래스를 파생 클래스(Derived Class)라고 한다. 기반 클래스를 부모 클래스, 슈퍼 클래스라고도 부르고 이에 따라 파생 클래스를 자식 클래스, 서브 클래스라고도 부른다. 클래스를 상속하는 방법은 다음과 같다.
class 기반클래스이름:
코드
class 파생클래스(기반클래스):
코드
아래는 사람이라는 기반클래스를 만들어 이를 상속받는 학생 클래스를 만드는 예제이다.
#derived.py
class Person:
def greeting(self):
print('안녕하세요.')
class Student(Person):
def study(self):
print('공부하기')
james = Student()
james.greeting() # 안녕하세요.: 기반 클래스 Person의 메소드 호출
james.study() # 공부하기: 파생 클래스 Student에 추가한 study 메소드
Student 클래스를 james에 저장한 다음 james를 이용해 sutdent 클래스의 메소드인 study도 사용하고, 상속받은 클래스인 Person의 메소드인 greeting도 사용 가능하다.
부모 클래스와 자식 클래스에 둘 다 __init__ 메소드가 정의 되어 있을 때, 자식 클래스를 인스턴스에 저장하여 사용하면, 자식 클래스의 __init__은 호출되지만, 부모 클래스의 __init__은 호출되지 않는다.
class Person:
def __init__(self):
print('Person __init__')
self.hello = '안녕하세요.'
class Student(Person):
def __init__(self):
print('Student __init__')
self.school = '파이썬 코딩 도장'
james = Student()
print(james.school)
print(james.hello) # 기반 클래스의 속성을 출력하려고 하면 에러가 발생함
이 때에는 자식클래스의 __init__ 메소드에 부모 클래스의 __init__을 호출해주는 코드를 한 줄 추가해줌으로써 해결할 수 있다.
class Person:
def __init__(self):
print('Person __init__')
self.hello = '안녕하세요.'
class Student(Person):
def __init__(self):
print('Student __init__')
super().__init__() # super()로 기반 클래스의 __init__ 메서드 호출
self.school = '파이썬 코딩 도장'
james = Student()
print(james.school)
print(james.hello)
자식 클래스에서 부모 클래스의 속성이나 메소드에 접근할 때에는 super(). 을 이용하면 된다.
오버라이딩
부모 클래스에 정의 된 메소드를 자식 클래스에서 재정의 하는 것을 오버라이딩이라고 한다. 아래에는 Person에서 정의한 Greeting 메소드를 자식 클래스에서 재정의 한 예시 코드이다.
#Overriding.py
class Person:
def greeting(self):
print('안녕하세요.')
class Student:
def greeting(self):
print('안녕하십니까?')
james = Student()
james.greeting()
$ python Overriding.py
안녕하십니까?
greeting을 호출하면 Student의 greeting이 호출되는 것을 볼 수 있다. 이런 경우엔 부모 클래스의 Greeting이 사용이 안되는데, 만약 부모 클래스의 greeting도 함께 사용하고자 한다면, 자식 클래스의 greeting 메소드를 다음과 같이 수정해주면 된다.
class student(Person):
def greeting(self):
super().greeting()
print('안녕하십니까?')
'Programming > Python' 카테고리의 다른 글
[ProjectH4C] 코딩도장 Python Write-up (3) (0) | 2020.08.11 |
---|---|
[ProjectH4C] 코딩도장 Python Write-up (1) (0) | 2020.08.06 |