#!/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()