#!/usr/bin/env python
# coding: utf-8
# # **Python Book1**
# **[효율적 개발로 이끄는 파이썬 실천기술 Jupyter Notebook](https://nbviewer.org/github/Jpub/fulfillPython/tree/main/)**
#
#
#
# ## **Chapter 4 데이터 구조**
# ### 01 **dict()**
# In[1]:
dict(note=1, notebook=2, sketchbook=3)
# In[2]:
items = {'note':1, 'notebook':2, 'sketchbook':3 }
items['book'] = 4
del items['note'] # dict() 객체에서 제외 : 별로 출력은 없음
items.pop('notebook') # dict() 객체에서 제외 : 해당 value 를 출력
# In[3]:
print(items)
items.get('note', "None Existed") # .get() : key의 값을 호출, 없으면 뒤의 값 출력
# In[4]:
# .update(dict()) : dict() 객체 합치는 메서드
items_two = {'notes':10}
items.update(items_two)
items
# In[5]:
'book' in items
# In[6]:
'note' not in items
# ### 02 **set()**
# In[7]:
items = {'note', 'notebook', 'sketchbook'}
items.add('book')
items
# In[8]:
items.pop() # 순서와 Key 가 별도로 없음
# In[9]:
items
# ### 03 **user Function()**
# In[10]:
def increment(page_number, last):
r"""page_number + 1 값이 last 값을 넘어가는지 확인"""
next_page = page_number + 1
if next_page <= last:
return next_page
raise ValueError('Invalid arguments')
increment(2, 10)
# ### 04 **lambda()**
# 이름이 없는 함수
# In[11]:
increment = lambda num: num+1
print(increment)
increment(3)
# In[12]:
nums = ['one', 'two', 'three']
filtered = filter(lambda x : len(x) == 3, nums)
list(filtered)
# In[13]:
# Check if given numbers are in range using lambda function
test = lambda x : True if (x > 10 and x < 20) else False
print(test(3))
print(test(12))
print(test(24))
# https://thispointer.com/python-how-to-use-if-else-elif-in-lambda-functions/
# https://stackoverflow.com/questions/60261960/how-to-use-lambda-if-else-in-map-on-list-in-python
list(map(lambda x : 'ok' if x == "apple" else None, ['apple', 'banana', 'cherry']))
# ### 05 **타입 힌트**
# In[14]:
# (p127) Optional : None 의 가능성이 있을 때 사용한다
from typing import Optional
def incerment(
page_num: int,
last: int, *,
ignore_error: bool = False) -> Optional[int]:
next_page = page_num + 1
if next_page <= last:
return next_page
if ignore_error:
return None
raise ValueError('Invalid arguments')
incerment.__annotations__
# increment(1, 3, ignore_error=1)
# In[15]:
def decrement(page_number:int) -> int:
previous_page: int
previous_page = page_number - 1
return previous_page
decrement(2)
# In[16]:
def increment(
page_number: int,
last:int,
*,
ignore_error: bool = False) -> Optional[int]:
next_page = page_number + 1
if next_page <= last:
return next_page
if ignore_error:
return None
raise ValueError("Invalid arguments")
increment(1, 10, ignore_error=1)
# ## **Chapter 6-1 클래스와 인스턴스**
# **[Jupyter Notebook](https://nbviewer.org/github/Jpub/fulfillPython/blob/main/06-classes/interactive.ipynb)**
# In[17]:
# 클래스 객체 만들기
class Page:
def __init__(self, number, content):
self.number = number
self.content = content
def output(self):
return f'{self.content}'
Page
# In[18]:
# 클래스 인스턴스 만들기
title_page = Page(0, 'Python Practice Book')
type(title_page)
# In[19]:
# 클래스 객체의 인스턴스 동일성 확인
isinstance(title_page, Page)
# In[20]:
" , ".join(dir(title_page))
# In[21]:
title_page.output()
# ## **Chapter 6-2 Class Method**
# - `__NEW__` 의 `cls` : **(p 138)**
# - 주 용도는 **입력 파라미터값을 인스턴스 생성시** 연산
# - `cls` 를 **클래스 메서드(Class Method)** 라고 한다
# - `__INIT__` : 이니셜라이저 는 인스턴스
# - `__new__` 출력값은 `__init__` 의 첫번째 인수 `self` 로 전달 받는다
# In[22]:
class Klass:
# new 의 첫번째 인수 : 클래스의 Method
# @classmethod 대신 __new__ 를 적용
def __new__(cls, *args):
r"""cls : 클래스"""
print(f"{cls=}")
print('new', args)
data = args
return super().__new__(cls)
def __init__(self, *args):
print('init', args)
# 인스턴스화
kls = Klass(1,2,3)
# In[23]:
# Class 파라미터 __init__ 전달 (`__new__` -> `__init__`)
# Class parametor => `cls` 로 __new__ => `self` 로 __init__ 전달
# 1) Key 를 지정 안한 경우 : 순차적 Key 값을 적용
# 2) Key 를 지정한 경우 : Key 에 맞게 대입된다
class Craft:
def __new__(cls, *args, **kwargs):
print ("Creating Instance")
return super().__new__(cls) # instance
def __init__(self, a, b):
self.a = a
self.b = b
i = Craft(2, 3)
print(i.a, i.b)
i = Craft(a=1, b=2)
print(i.a, i.b)
# In[24]:
# __new__() 사용시 주의할 점
# :: 인스턴스 생성시 값을 출력한다
class Evil:
def __new__(cls, *args):
return 1
evil = Evil()
isinstance(evil, Evil)
# ## **Chapter 6-3 프로퍼티 1 (getter, setter)**
# > **다른 변수의 값** 을 **Class 인스턴스** 에서 활용하는 용도로 사용한다 ([Setter 와 Getter 개념의 이해](https://jinmay.github.io/2019/11/23/python/python-class-first/))
# - **getter** 메소드 : **값을 얻어올 때** 사용하는 기능으로 **@property** 를 사용
# - **setter** 메소드 : **읽은 값** 을 **쓰는** 기능으로 **@변수.setter** 를 사용
# In[25]:
class Book:
def __init__(self, raw_price):
if raw_price < 0:
raise ValueError('price must be positive')
self.raw_price = raw_price
self._discount_value = 0 # 초깃값
@property
def discount(self):
return self._discount_value
@discount.setter
def discount(self, value):
if value < 0 or 100 < value:
raise ValueError(
'discounts must be between 0 and 100')
self._discount_value = value
@property
def price(self):
multiple = 100 - self._discount_value
return int(self.raw_price * multiple / 100)
book = Book(2000)
book.price, book.discount
# In[26]:
book.discount = 20
book.price
# ## **Chapter 6-3 프로퍼티 2 (히든 속성)**
# > 변수이름 앞 **`_`** 는 **외부에 공개할 필요 없는** 프라이빗 변수이나, **약속에 불과** 해서 외부서 참조가능
#
# > **하지만**, 변수이름 앞 **`__`** 두개는 **클래스 이름을 덧붙인 변수** 즉 `_클래스명__변수명` 으로 자동 변환된다
# In[27]:
class Klass:
def __init__(self, x):
self.__x = x
kls = Klass(10)
try:
kls.__x
except Exception as e:
import termcolor # termcolor.COLORS
print(termcolor.colored(e, 'red'))
kls._Klass__x
# ## **Chapter 6-4 [클래스 메서드](https://nirsa.tistory.com/113)** [개념 익히기]
# - **정적 메소드 (@staticmethod)** 는 **부모 의 원본 값** 을 호출한다
# - **클래스 메소드 (classmethod)** 는 **자식에서 변경된 값** 을 호출한다
# - **자식객체** 에서 **부모객체의 값 getter** 와 **setter**
# - 때문에 **부모 클래스의 인스턴스** 에서는 **동일** 하게 보입니다.
# In[28]:
# 부모 클래스의 인스턴스 에서는 동일하게 보인다..
class test:
def instance_add(self, a, b):
return a + b
@staticmethod # 정적 메서드는 self 불필요
def static_add(a, b):
return a - b
@classmethod # 클래스 메서드는 cls 를 첫번째 인자로 사용
def class_add(cls, a, b):
return a * b
a = test
print(a.instance_add(None, 4, 4)) # 원본 함수의 내용
print(test.static_add(4, 4)) # 정적메서드 내용
print(test.class_add(4, 4)) # 클래스메서드 내용
# In[29]:
## 하지만 이를 상속한 자식 클래스에서는 차이가 발생한다
# 클래스 메소드 : cls로 `현재 자기가 실행된 클래스 속성` 을 가져온다
# 정적 메소드 : 부모 클래스(Windows)의 속성을 가져온다
class Windows:
os = "window10" # 클래스 속성
def __init__(self):
self.out = "OS: " + self.os
def os_output(self):
print(self.out)
@staticmethod # 부모원본
def static_os():
return Windows()
@classmethod # 자식의 변경내용
def class_os(cls):
return cls()
class Linux(Windows):
os = "Linux" # 클래스 속성
Linux.static_os().os_output() # 부모의 원본을 호출
Linux.class_os().os_output() # 자식의 변경된 내용을 호출
# ## **Chapter 6-4 클래스 메서드 2 (`@classmethod`)**
# **@classmethod** 클래스에 속한 메서드로, 첫번째 인수에 클래스 객체를 전달 합니다
# - **@classmethod** 를 붙이는 것을 제외하면, 다른 메서드와 동일
# - 첫 번째 인수가 클래스 객체로 self 가 아닌, `cls` 이름을 사용
# In[30]:
from operator import attrgetter
class Page:
book_title = 'Python Practice Book :: ======'
def __init__(self, number, content):
self.number = number
self.content = content
def output(self):
return f'{self.content}'
@classmethod
def print_pages(cls, *pages):
print(cls.book_title)
pages = list(pages)
# key=attrgetter('number') : sorted() 함수의 정렬기준
for page in sorted(pages, key=attrgetter('number')):
print(page.output())
first = Page(1, 'first page')
second = Page(20, 'second book page')
third = Page(3, 'third page')
first
# In[31]:
# 클래스를 활용한 호출
Page.print_pages(first, second, third)
# In[32]:
# 인스턴스 객체를 활용한 호출
first.print_pages(first, second, third)
# In[33]:
from operator import attrgetter
class Page:
book_title = 'Python Practice Book :: ======'
def __init__(self, number, content):
self.number = number
self.content = content
def output(self):
return f'{self.content}'
@classmethod
def print_pages(cls, *pages):
print(cls.book_title)
pages = list(pages)
# key=attrgetter('number') : sorted() 함수의 정렬기준
for page in sorted(pages, key=attrgetter('number')):
print(page.output())
first = Page(1, 'first page')
second = Page(20, 'second book page')
third = Page(3, 'third page')
first
# In[34]:
# 클래스를 활용한 호출
Page.print_pages(first, second, third)
# In[35]:
# 인스턴스 객체를 활용한 호출
first.print_pages(first, second, third)
# In[36]:
first.content
# ## **Chapter 6-5 클래스 메서드 3 (`@staticmethod`)**
# - 위의 클래스 메서드와 거의 같은 구문이다
# - 인스턴스 및 클래스 객체로는 전달되지 않고, 호출시에만 전달값을 출력한다
# In[37]:
class Page:
def __init__(self, number, content):
self.number = number
self.content = content
@staticmethod
def check_blank(page):
return bool(page.content)
page = Page(1, '')
Page.check_blank(page)
# ## **Chapter 6-6 클래스의 상속**
# `class Inheritance`
# - `Child Class` 에서 `Base Class` 가 갖는 Method 를 그래도 사용하면서 변수등을 추가한다
# - `__str__` 와 같은 내장 타입은 사용자 클래스도 동일하게 서브 클래스로 정의된다
# In[38]:
class Page:
def __init__(self, number, content):
self.number = number
self.content = content
def output(self):
return f'{self.content}'
# Method Override : Page 클래스를 통으로 상속
class TitlePage(Page):
def output(self):
title = super().output()
return title.upper()
title = TitlePage(0, 'Python Practice Book')
print(title.output())
title.number, title.content
# In[39]:
class Length(float):
def to_cm(self):
return super().__str__() + ' cm'
def to_inch(self):
return super().__str__() + ' inch'
pencil_length = Length(16)
pencil_length.to_cm(), pencil_length.to_inch()
# ## **Chapter 6-7 다중상속**
# `Multiple Inheritance`
# - **완결성이 있는 원본 클래스(Base Class)** 에, **추가 클래스로 메서드를 추가하는** 용도
# - 추가 클래스의 `self` 는 **원본 클래스의 요소와 메서드** 를 연결해야, 자식클래스가 잘 작동한다
# - Base Class 에서 `,` 로 여러개를 구분한다
# In[40]:
# 부모 클래스1
class Page:
def __init__(self, number, content):
self.number = number
self.content = content
def output(self):
return f'{self.content}'
# 부모 클래스
class HTMLPageMixin:
def to_html(self):
return f'{self.output()}'
class WebPage(Page, HTMLPageMixin):
pass
page = WebPage(0, 'web content')
page.to_html()
# ## **Chapter 7 모듈, 패키지, 스코프**
# ### 01 **클로저 `nonlocal`**
# 외부에 있는 변수를 참조한다
# In[41]:
def counter():
count = 0
def _increase():
nonlocal count
count += 1
return count
return _increase
counter1 = counter()
counter1, counter1(), counter1()