Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions Chapter1/pythonic_code(1).md
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
## 1. 컴프리헨션 문법
> 파이썬의 자료구조 데이터를 **간결하게** 생성할 수 있는 문법

### 리스트 컴프리헨션
> 리스트를 만드는 간결한 방법을 제공

```python
numbers = []
for i in range(5):
numbers.append(i*2)

#컴프리헨션 표현
numbers = [i*2 for i in range(5)]
# [0, 2, 4, 6, 8]

#조건문을 넣어도 됨
even = [i for i in range(10) if i % 2 == 0]
# [0, 2, 4, 6, 8]

#if 중첩도 가능
data = [i for i in range(1,11) if i % 2 == 0 if i<5]
# [0, 2, 4]
```

```python
date = [(x,y) for x in range(1,6) for y in range(1,4)]

# for 문과 비교
date2 = []
for i in range(1,6):
for j in range(1,4):
data2.append((i,j))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오타가 있어서 실행이 안될 것 같아요


# [(1, 1), (1, 2), (1, 3),
# (2, 1), (2, 2), (2, 3),
# (3, 1), (3, 2), (3, 3),
# (4, 1), (4, 2), (4, 3),
# (5, 1), (5, 2), (5, 3)]
```

### 딕셔너리 컴프리헨션

**유의할 점 : for문 앞에 키:값을 적는다는 것과 대괄호가 중괄호로 바뀐 것**

```python
squares = [x: x**2 for x in range(5)]
# {0:0, 1:1, 2:4, 3:9, 4:16}
```

zip 함수 결합
```python
name = ["왕춘삼", "김덕팔", "황갑득"]
age = [23, 14, 42]

data = {key: value for (key, value) in zip(name, age) if value > 20}

# 출력결과
{'왕춘삼': 23, '황갑득': 42}
```

### 셋(집합) 컴프리헨션

**유의할 점 : 딕셔너리 컴프리헨션과 비슷, 키:값 형태가 아닌 것만 유의**

```python
unique = {x%3 for x in range(10)}
# {0, 1, 2}
```

- 반복문 + 조건문을 한 줄로 표현가능
- 가독성과 효율성 향상
- **단, 복잡한 로직은 가독성을 해침**


참고 문서
https://tibetsandfox.tistory.com/25
94 changes: 94 additions & 0 deletions Chapter1/pythonic_code(2).md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
## 2. 언패킹(Unpacking)과 제너레이터(Generator)

### 언패킹
> 여러 값을 한 번에 변수에 풀어 넣는 기능

```python
a, b, c = [1, 2, 3]
print(a,b,c)
# 1 2 3

#리스트나 튜플 등 반복 가능한 객체면 다 가능
numbers = [1, 2, 3, 4, 5]
first, *middle, last = numbers
print(first, middle, last)
# 1 {2,3,4}, 5
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

실제로 이렇게 나오나요?

```

### 제너레이터
>데이터를 필요할 때만 하나씩 생성하는 지연평가 방식 함수
```python
def count_up_to(n):
for i in range(1,n+1):
yield i

for num in count_up_to(5):
print(num)
#1, 2, 3, 4, 5
```
함수 안에 yield 키워드를 사용하면 제너레이터이다.
yield가 값을 생성(반환)하고, 함수의 상태는 유지된 채 중단되었다가
다음 호출로 그 지점에서 재개된다.

yield 키워드
```python
def func():
yield 1
yield 2
yield 3

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

   def gen_with_return():
       yield 1
       yield 2
       return "끝"  # 이 값은 어떻게 접근할까요?
       yield 3  # 이건 실행될까요?

foo = func()

print(next(foo)) # 1
print(next(foo)) # 2
print(next(foo)) # 3
```
### return 과의 차이점

제너레이터 함수는 yield 를 통해 값을 반환.
next 메소드나 for문 순회 등을 통해 값들을 리턴받을 수 있음.
**yield가 호출된다고 해서 함수가 종료되는 것은 아님**

제너레이터 내부에서 return을 사용하면 현재 값에 상관없이 StopIteration 예외 발생

```python
def func():
print("1 리턴 전")
yield 1
print("1 리턴 후, 2 리턴 전")
yield 2
print("2 리턴 후, 3 리턴 전")
yield 3
print("3 리턴 후")


foo = func()

print(next(foo))
print(next(foo))
print(next(foo))

1 리턴 전
1
1 리턴 후, 2 리턴 전
2
2 리턴 후, 3 리턴 전
3
```


표현식 : 리스트 컴프리헨션과 동일하지만 **[] 대신 () 사용**
-> 값을 즉시 모두 만들지 않음

```python
lst=[x*2 for x in range(5)]
gen=(x*2 for x in range(5))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

list(gen) # [0, 2, 4, 6, 8]
list(gen) # ??? -> 여기서 뭐가 나올까요?

```

- 전체 시퀀스를 메모리에 올리지 않음 -> 큰 파일, 로그, 스트리밍 데이터 처리에 적합
- 필요할 때만 계산 -> 파이프라인에서 불필요한 계산 회피
- 로컬 변수로 상태를 유지하면서 반복 로직 구현 가능
- **디버깅 복잡, 한 번만 소비 가능. 다시 쓰려면 재생성**

참고 문서
https://tibetsandfox.tistory.com/28
53 changes: 53 additions & 0 deletions Chapter1/pythonic_code(3).md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
## 매직 메서드
> 클래스에 특수 동작을 정의하는 메서드 (자동 호출됨) / `__이름__` 형식

- `__str__` vs `__repr__`

```python
class User:
def __init__(self, name):
self.name = name

def __str__(self):
return f"User: {self.name}" # print()용

def __repr__(self):
return f"User(name='{self.name}')" # 개발자용 (디버깅)

user = User("Gahee")
print(user) # User: Gahee
user # User(name='Gahee')
```

→ print(user) → `__str__` 호출

→ user → `__repr__` 호출 : 대화형 콘솔 or Jupyter 등

→ repr(user) → 명시적으로 `__repr__` 호출

→ str(user) → 명시적으로 `__str__` 호출

→ `__str__` 가 없으면 자동으로 `__repr__` 사용


- `__eq__` (== 비교 연산자 오버라이드)

```python
class Point:
def __init__(self, x, y):
self.x, self.y = x, y

def __eq__(self, other):
return self.x == other.x and self.y == other.y
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

__eq__를 구현하면 왜 __hash__도 필요할까?

set이나 dict의 키로 사용할 때 어떤 문제가 생길까요?
Python은 왜 자동으로 해시를 무효화할까요?


p1 = Point(1, 2)
p2 = Point(1, 2)
print(p1 == p2) # True
```

→ a == b 를 하면, 내부적으로 호출

→ 클래스에 `__eq__`를 안 만들면 ==는 ‘객체가 같은 메모리 주소를 가리키는지’ 비교

- `__add__` : a + b 를 하면 내부적으로 실행
- `__len__` : len() 실행하면 내부에서 진짜 리스트의 길이를 반환
124 changes: 124 additions & 0 deletions Chapter1/pythonic_code(4).md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
## 데코레이터(Decorator)
> 어떤 함수를 인자로 받아 꾸며준 후 다시 함수로 리턴하는 함수
> 함수 내부에 변화 주지 않고 로직을 추가하고 싶을 때 사용
**데코레이터는 꾸며주는 함수 내부에 직접적 수정이나 로직 변환 불가**

```python
def hello():
print("안녕!")

def decorator(func):
def wrapper():
print("함수 실행 전")
func()
print("함수 실행 후")
return wrapper

decorated = decorator(hello)
decorated()

# 함수 실행 전
# 안녕!
# 함수 실행 후
```
어떤 함수를 데코레이터로 꾸며주려면 그 함수의 선언부 위에 @데코레이터명을 적어주면 됨
@ 문법으로 더 깔끔하게

```python
def decorator(func):
def wrapper():
print("시작")
func()
print("끝")
return wrapper

@decorator
def greet():
print("안녕 파이썬!")

greet()

# 시작
# 안녕 파이썬!
# 끝ㅁ
```

예시 추가
```python
def say_hello():
print("Hello")

def decorator(func):
def sentence(*args, **kwargs):
print("Nice to meet you")
return func(*args, **kwargs)

return sentence

@decorator
def say_hello():
print("Hello")

say_hello()

# 실행 결과
Nice to meet you
Hello
```

데코레이터 동작 파악
```python
def decorator(func): #1
def sentence(*args, **kwargs): #3
print("Nice to meet you") #6
return func(*args, **kwargs) #7

return sentence #4

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

functools.wraps에 대해 좀 더 찾아보면 좋을 것 같아요.

@decorator #3
def say_hello(): #2
print("Hello") #8


say_hello() #5
```
#1 decorator 함수를 선언
#2 say_hello 함수 선언
#3 say_hello 함수를 decorator 함수로 꾸며줌. 이는 variable = decorator(say_hello)와 동일함. 따라서 sentence 함수도 이때 선언
#4 decorator 함수로 꾸며주었기에 say_hello 함수는 sentence 함수의 로직을 실행
#5 say_hello 함수 실행
#6 say_hello 함수는 sentence 함수의 로직을 가지고 있기 때문에 sentence 함수의 로직 실행
#7 decorator로 꾸며준 함수인 say_hello 함수를 호출하여 리턴
#8 호출된 say_hello 함수가 실행

class와 함께 사용도 가능
```python
class Decorator:
def __init__(self, func): # 꾸며줄 함수를 매개변수로 전달
self.func = func # 꾸며줄 함수를 속성에 저장

def __call__(self, *args, **kwargs):
print("Nice to meet you")
self.func() #속성에 저장된 함수 호출
print(f"My name is {args[0]}")


@Decorator
def say_hello():
print("Hello")


say_hello("Fox")

#실행 결과
Nice to meet you
Hello
My name is Fox
```

- 코드의 중복을 최소화하고 재사용성을 높일 수 있음
- 가독성 높일 수 있음. 로직 수정이 필요할 때도 데코레이터만 수정하면 됨
- **중첩해서 사용하면 디버깅 난이도 상승**

참고문서
https://tibetsandfox.tistory.com/10