goormNLP [Python Programming]
Auspice by Goorm, Manage by DAVIAN @ KAIST
Lecture 7: Pythonic Programming
Comprehension
- List, Dictionary 등을 빠르게 만드는 기법.
- for + append 보다 속도 빠름.
- ```python result = [] for i in range(10): result.append(i * 2)
example
result = [i * 2 for i in range(10)]
if 문을 마지막에 달아 원하는 요소만 추가 가능.
evens = [i for i in range(100) if i % 2 == 0]
겹 for문 사용 가능.
result = [(i,j) for i in range(5) for j in range(i)]
다차원 배열 만들기 매우 유용.
eye = [[int(i==j) for j in range(5)] for i in range(5)]
### Generator
- 요소를 하나씩 생성해서 반환하는 객체를 **Generator**라고 함.
example) **range** 함수의 경우 숫자를 하나씩 생성하여 반환.
```python
def my_range(stop):
number = 0
while number < stop:
yield number # function에 yield를 사용할 시 Generator가 됨.
number += 1 # yield 하는 위치에서 값을 반환. 다시 값을 요청 받을 시
# yield 다음 줄부터 실행. Return 될 시 반복을 멈춤.
for i in my_range(5):
print(i)
Sequence 전체를 생성하는 것이 아니므로 메모리 효율적. 매우 큰 데이터 셋을 처리할 땐 Generator 사용 권장.
Built-in Functions
sum, any, all, max, min, zip, enumerate, Lambda Function, map, filter …
zip
- 2개 이상의 순환 가능한 객체를 앞에서부터 한번에 접근할 때 사용.
seq1 = ['What', 'is', 'zip']
seq2 = [True, False, True]
seq3=[1,2,3,4] #길이가 안 맞을 경우 남는 건 버림
for w1, w2, w3 in zip(seq1, seq2, seq3): # 앞에서 부터 하나씩 빼어 Tuple로 반환
print(w1, w2, w3)
>>> What True 1
>>> is False 2
>>> zip True 3
enumerate
- For문이 Sequence를 돌 때 해당하는 idex가 필요할 때 사용함.
- zip과 enumerate를 동시에 사용하는 등 여러 Generator를 한번에 사용.
seq1 = ['This', 'sentence']
seq2 = [True, False]
for i, (a, b) in enumerate(zip(seq1, seq2)):
print(i, a, b)
>>> 0 This True
>>> 1 sentence False
Lambda Function
- 함수의 이름 없이 빠르게 만들어 쓸 수 있는 익명 함수.
- 여러 줄을 쓸 수 없음.
- 공식적으로는 Lambda의 사용을 권장하지는 않지만, 많이 씀. 문서화 지원 미비. 이름이 존재하지 않는 함수가 생성. 복잡한 함수 lambda로 작성할 시 가독성 하락.
def add(a,b):
return a + b
add = lambda a, b: a + b
Lecture 8: Object-Oriented Programming (OOP)
-
방법 1: 절차 지향 프로그래밍 교수용 프로그램과 학생용 프로그램을 따로 만듦. 수강 신청과정을 처음부터 끝까지 작성.
-
방법 2: 객체 지향 프로그래밍 (OOP) 수강신청을 하는 주체들(학생, 교수)의 행동과 데이터를 정의. 각 개체들을 엮어 유기적인 수강신청 API 작성.
OOP Example
Class (설계도)
- Attribute (데이터, 속성)
- 이름
- 학번
- 현재 수강중인 강의
- Method (행동)
- 특정 강의를 수강하기.
- 특정 강의를 드랍하기.
Instance (객체)
- Attribute (데이터, 속성)
- 이창석
- 2015000000
- CS241
- Method (행동)
- 특정 강의를 수강하기.
- 특정 강의를 드랍하기.
Class
Class Example
class Student(object):
SCHOOL = "goorm" # 클래스 속성 (Class attribute)
def __init__(self, name: str, sid: int): # 생성자
self.name = name # 속성 (Attribute)
self.sid = sid
self.classes = set()
# 클래스 함수 (Method)
def take_class(self, class_name: str) -> None:
self.classes.add(class_name)
def drop_class(self, class_name: str) -> None:
self.classes.discard(class_name)
- 학생 객체(Instance)를 만들고 써보기.
changseok_lee = Student('ChangSeok Lee', 2015000000)
# 속성 출력
print(changseok_lee.name, "in", Student.SCHOOL)
>>> ChangSeok Lee in goorm
changseok_lee.take_class("CS101")
changseok_lee.take_class("CS202")
changseok_lee.drop_class("CS101")
print(changseok_lee.classes)
>>> {'CS202'}
Class Declaration: Class 선언부
- 클래스 이름은 CamelCase가 관습적으로 사용됨. (ex: NormalCase.. 낙타 등 모양)
- 부모 클래스가 지정되지 않았을 시 object가 자동 상속됨.
class Student(object): 예약어(class) / 클래스 이름(Student) / 부모 클래스(object)
Class Attribute: Class 속성
class Student(object):
SCHOOL = 'goorm' # 클래스 속성 (Class attribute)
- 클래스 전체가 공유하는 속성 값.
- 모든 객체(instance)가 같은 값을 참조.
- 남용하면 스파게티 코드의 원인이 됨.
Class.attr 형태로 접근. (속성 출력)
print(Student.SCHOOL)
>>> goorm
Method: Class 함수
class Student(object):
# 클래스 함수 (Method)
def take_class(self, class_name: str) -> None:
self.classes.add(class_name)
- 각 객체에 적용이 가능한 함수.
- 현재 수정하고자 하는 객체를 “self“로 지칭. (관습) 파이썬은 “self”를 첫번째 파라미터로 명시적으로 받음.
- Class.method(instance, args, ..) 혹은 instance.method(args, …)
# 메소드 실행 [ instance.method(args, ...) ]
changseok_lee.take_class("CS101")
Class Attribute: 객체 속성
class Student(object):
def __init__(self, name: str, sid: int): # 생성자
self.name = name # 속성 (Attribute)
- 각각의 객체가 개인적으로 가지는 값.
- instance.attr의 형태로 접근
- class 형태로 선언되어 나온 객체는 언제 어디서는 attribute 수정 가능.
Magic Method
- 메소드 이름이
__METHOD__
형태일 경우 특별한 Magic Method.
Initializer
생성자 : __init__
class Student(object):
def __init__(self, name: str, sid: int): # 생성자
self.name = name # 속성 (Attribute)
self.sid = sid
self.classes = set()
- 객체를 생성할 때 호출됨.
- 일반적으로 객체의 속성을 초기화 하는데 사용.
- Class(args, ..) 형태로 호출하여 객체 생성.
changseok_lee = Student('ChangSeok Lee', 2015000000)
- 거의 유일하게 정해진 Argument format이 없는 Magic Method.
Destroyer
소멸자 : _del_
class Student:
def __del__(self):
self.classes.clear()
- 객체를 소멸할 때 호출 됨.
- 파이썬은 Garbage Collection, GC로 메모리 관리. 객체가 어디에서도 참조되지 않을 때 객체가 소멸. 소멸 타이밍을 잡기 어려워 잘 사용되지 않음.
- del 명령어
del changseok_lee
변수 이름을 명시적으로 없애기 가능. 참조를 명시적으로 삭제하는 것이고, 객체를 명시적으로 삭제하는 것이 아님.
#### Indexing
Indexing 메소드 : __getitem__
, __setitem__
class DoubleMapper(object):
def __init__(self):
self.mapping = {}
def __getitem__(self, index): # Indexing get
return self.mapping.get(index, index * 2)
def __setitem__(self, index, item): # Indexing set
self.mapping[index] = item
mapper = DoubleMapper()
print(mapper[10], mapper[1, 2])
>>> 20 (1, 2, 1, 2)
mapper[10] = 15
print(mapper[10], mapper[1, 2])
>>> 15 (1, 2, 1, 2)
[]
indexing을 재정의.
#### Length
Length 메소드 : __len__
class Dataset:
def __init__(self, data, times=3):
self.data = data
self.times = times
def __len__(self): # len(instance) 호출될 시 호출
return len(self.data) * self.times
def __getitem__(self, index):
if index > len(self):
raise IndexError()
return self.data[index % len(self.data)]
dataset = Dataset([10, 2, 5, 2], times=5)
print(len(dataset))
>>> 20
#### Typing
- 객체를 다른 타입으로 형 변환할때 호출.
- 이외에도
__int__
,__float__
,__bool__
등 존재.
class Student:
def __init__(self, name: str, sid: int):
self.name = name
self.sid = sid
def __str__(self): # str 형변환
return self.name + '_' + str(self.sid)
changseok_lee = Student("ChangseokLee", 2015000000)
print(changseok_lee) # str 형태 출력
>>> ChangseokLee_2015000000
#### Comparison Operator
- A < B를 호출 ->
A.__lt__(B)
를 호출. ( lt == less than ) - 이외에도
__le__
,__gt__
,__ge__
,__eq__
,__ne__
가 존재.
class Student:
def __init__(self, name: str, sid: int):
self.name = name
self.sid = sid
def __lt__(self, other):
return self.sid < other.sid # < 연산자를 재정의.
students = [
Student("Lee", 1234,
Student("Chang", 4566),
Student("Seok", 2267)
]
print(*[student.name for student in sorted(students)]) # Sorted 사용 가능
>>> Lee Seok Chang
#### Arithmetic Operator
class MyComplex:
def __init__(self, real, imaginary):
self.real = real
self.imaginary = imaginary
def __str__(self):
return str(self.real) + '+' + str(self.imaginary) + "j"
def __add__(self, other): # + 연산자 재정의
return MyComplex( self.real + other.real,
self.imaginary + other.imaginary) # Out-place 연산
a = MyComplex(3, -5)
b = MyComplex(-6, 7)
print(a + b)
>>> -3+2j
#### Callable
함수화 메소드 : __call__
class AdditionNumber(object):
def __init__(self, number: int):
self.number = number
def __call__(self, number: int):
return number + self.number
addition_5 = AdditionNumber(5)
print(addition_5(10))
>>> 15
- 생성된 객체를 호출 가능하게 만듦.
- instance(agrs, …)가 instance.
__call__
(args, …)를 호출.
Iterable
# 평소 사용하는 for문
seq = [1,2,3,4,5]
for elem in seq:
print(elem, end=' ')
>>> 1 2 3 4 5
# for문이 내부에서 돌아가는 원리.
seq = list([1,2,3,4,5])
iterable = iter(seq)
while True:
try:
elem = next(iterable)
except StopIteration:
break
print(elem, end=' ')
>>> 1 2 3 4 5
실제 for문에서 일어나는 일은?
- iter 내장 함수 해당 객체의 순환자 반환.
__iter__
호출.
-
next 내장 함수 해당 순환자를 진행.
__next__
호출. - 끝에서 StopIteration Exception
- Generator는 자동으로
__iter__
와__next__
가 구현.
Context Manager
class Student:
def __init__(self, name, sid):
self.name = name
self.sid = sid
def __enter__(self): # with 구문에 들어갈 때 사용, return 값이 as 이하로 할당
self.classes = set()
return self
def __exit__(self, exc_type, exc_value, trace): # with 구문 나갈 때 사용
self.classes.clear()
CS_Lee = Student("LCS", 2015)
with CS_Lee:
CS_Lee.classes.add('CS201')
with Student("LCS", 2015) as CS_Lee:
CS_Lee.classes.add('CS201')
- 소멸자 대용으로 특정 Block 입장/종료 시 자동으로 호출.
- File description 등을 자동으로 닫고자 할 때 사용.
#### Property
값을 가져오는 Method를 getter. 값을 저장하는 Method를 setter.
- Property를 통해 getter와 setter를 명시적으로 설정 가능.
- Encapsulation 등에 활용.
class Person:
def __init__(self):
self.__age = 0
@property
def age(self): # getter
return self.__age
@age.setter
def age(self, value): # setter
self.__age = value
LCS = Person()
LCS.age = 27
print(LCS.age)
>>> 27
#### Static & Class Method 파이썬에는 2가지 정적 함수 존재.
- 객체에는 접근 불가능.
- 일반적으로 Class.method 형태로 사용.
-
Static Method @staticmethod 꾸밈자 사용. 특별한 argument를 받지 않음. 일반적으로 class 내 유틸 함수로 사용. Class를 일종의 Namespace로 사용.
-
Class Method @classmethod 꾸밈자 사용. 호출된 class인 cls를 받음. (self와 비슷) factory 패턴에서 사용
class Number:
Constant = 10
@staticmethod
def static_factory():
obj = Number()
obj.value = Number.Constant
return obj
@classmethod
def class_factory(cls):
obj = cls()
obj.value = cls.Constant
return obj
number_static = Number.static_factory()
number_class = Number.class_factory()
print(number_static.value, number_class.value)
>>> 10 10
상속하면 차이가 발생!
- @staticmethod는 유틸리티 성격에 맞게 사용 가능.
- @classmethod는 클래스의 어떤 옵션을 바꾸는 용도로 사용 가능.
## Three Elements of OOP
-
상속 (Inheritance) 기존에 구현되어 있는 틀을 상속받아 새로운 틀을 만드는 것. 기존의 틀: 부모 Class, 새로운 틀: 자식 Class 자식 Class에서는 부모의 기능을 이용할 수 있음.
-
다형성 (Polymorphism) 같은 이름의 메소드를 다르게 작성하는 것. 각 자식 클래스가 다른 클래스와 차별화 됨.
Python에서의 상속과 다형성
- 다중 상속 지원 => 죽음의 다이아몬드 [메소드 탐색 순서를 따름 (mro)]
- super 내장 함수를 이용하여 상위 클래스 접근 가능.
class Student:
def __init__(self, name: str, sid: int):
self.name = name
self.sid = sid
self.classes = []
def __str__(self):
return self.name + "_" + str(self.sid)
def take_class(self, class_name: str) -> None:
self.classes.append(class_name)
class Master(Student): # Student 상속
def __init__(self, name: str, sid: int, professor: str):
super().__init__(name, sid) # 부모 클래스 생성자 접근, 정해진 부르는 타이밍은 없다.
self.professor = professor
def __str__(self): # __str__ 재정의 → 다형성
return super().__str__() + "_" + str(self.professor)
master = Master('ChangseokLee', 2015, 'goorm')
print(master)
>>> ChangseokLee_2015_goorm
print(super(Master, master).__str__()) # super로 언제나 원하는 상위 클래스로 변환
>>> ChangseokLee_2015
- 가시성, 캡슐화 (Visibility)
가시성이란 사용자에게 모델의 내부를 감추는 것을 의미.
- 캡슐화, 정보 은닉, 클래스 간 간섭 최소화.
- 최소한의 정보만을 특정 API로 공개.
Python에서의 가시성.
- 명시적인 private & protected 범위가 없음 -> 모두 public
- private 변수/함수 이름 앞에
__
를 붙임.ex) self.__name
- protected 변수/함수 이름 앞에
_
를 붙임.ex) self._name
class TestClass(object):
def __init__(self):
self.attr = 1
self._attr = 2
self.__attr = 3
instance = TestClass()
__
의 경우 변수명 앞에 Class 이름을 넣어 Mangling - 자식과 이름이 안겹침.- private와 protected는 코드 완성 등에서 안보임.
- 그러나 둘 다 public과 기능적 차이는 없음. (밖에서 접근 가능함)
댓글남기기