강의영상

- (1/5) __init__

- (2/5) self의 의미

- (3/5) 파이썬의 비밀1

- (4/5) 파이썬의 비밀2

- (5/5) 숙제설명

imports

from PIL import Image
import requests

클래스 공부 2단계

__init__()

- STOOOP을 다시 복습

url1 = 'https://github.com/guebin/IP2022/blob/master/_notebooks/2022-05-07-stop1.jpeg?raw=true'
url2 = 'https://github.com/guebin/IP2022/blob/master/_notebooks/2022-05-07-stop2.png?raw=true' 
class STOOOP: 
    title = '학교폭력!' 
    url = url1
    end = '멈춰~~~~'
    def stop(self):
        print(self.title)
        display(Image.open(Image.io.BytesIO(requests.get(self.url).content)))
        print(self.end) 
s1=STOOOP() # STOOOP이라는 클래스에서 s1이라는 인스턴를 만드는 과정 
s1.title,s1.url,s1.end
('학교폭력!',
 'https://github.com/guebin/IP2022/blob/master/_notebooks/2022-05-07-stop1.jpeg?raw=true',
 '멈춰~~~~')
s1.stop()
학교폭력!
멈춰~~~~

- 왜 s1의 default title이 항상 "학교폭력"이어야 하는가? -> __init__의 개발

- 성능4: __init__()함수를 이용하여 "클래스->인스턴스"의 시점에서 수행하는 일련의 동작들을 묶어서 수행할 수 있음

class STOOOP: 
    #title = '학교폭력!' 
    url = url1
    end = '멈춰~~~~'
    def __init__(self,title): 
        self.title = title
    def stop(self):
        print(self.title)
        display(Image.open(Image.io.BytesIO(requests.get(self.url).content)))
        print(self.end) 

- 잘못된사용

s1=STOOOP() # 이 시점에서 __init__ 이 수행된다!
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Input In [15], in <cell line: 1>()
----> 1 s1=STOOOP()

TypeError: __init__() missing 1 required positional argument: 'title'

- 올바른사용

s1=STOOOP("수강신청매크로") # 이 시점에서 __init__ 이 수행된다!
s1.title,s1.url,s1.end
('수강신청매크로',
 'https://github.com/guebin/IP2022/blob/master/_notebooks/2022-05-07-stop1.jpeg?raw=true',
 '멈춰~~~~')
s1.stop()
수강신청매크로
멈춰~~~~

- 잘못된사용에서 에러가 발생한 이유?

TypeError: __init__() missing 1 required positional argument: 'title'
  • s1 = STOOOP() 이 실행되는 순간 __init__()이 내부적으로 실행된다.
  • 그런데 __init__()의 첫번째 입력인 self는 입력안해도 무방했음. 그런데 두번째 입력은 title은 입력을 해야했음.
  • 그런데 title을 입력하지 않아서 발생하는 에러

- __init__(self,arg1,arg2,...) 함수에 대하여

  • 엄청나게 특별해 보이지만 사실 몇가지 특별한 점을 제외하고는 어떠한 마법도 없는 함수이다.
  • 특별한점1: 첫번째 입력으로 반드시 self를 넣어야함. (이건 사실 클래스 내의 메소드 거의 다 그러함)
  • 특별한점2: 클래스에서 인스턴스를 만드는 시점에 자동으로 실행된다.
  • 특별한점3: __init__(self,arg1,arg2,...)의 입력중 self 이외의 입력들은 "클래스->인스턴스"의 시점에서 "인스턴스이름 = 클래스이름(arg1,arg2,...)"와 같이 사용한다. (이 예제의 경우 STOOOP(title)와 같이 사용해야함)

- title이 디폴트로 들어가는 상황도 불편했지만, title을 명시적으로 넣지 않으면 에러가 발생하는 것도 불편하다?

class STOOOP: 
    #title = '학교폭력!' 
    url = url1
    end = '멈춰~~~~'
    def __init__(self,title=None): 
        self.title = title
    def stop(self):
        print(self.title)
        display(Image.open(Image.io.BytesIO(requests.get(self.url).content)))
        print(self.end) 
s2 = STOOOP()
s3 = STOOOP('KOSPI 하락')
s2.stop()
None
멈춰~~~~
  • 제목이 없으면 없는대로 출력가능
s3.stop()
KOSPI 하락
멈춰~~~~

self의 의미

- 이전 예제를 다시 복습

class Klass4:
    n = 1
    url = 'https://github.com/guebin/IP2022/blob/master/_notebooks/2022-05-07-stop1.jpeg?raw=true'
    def show(self):
        display(Image.open(Image.io.BytesIO(requests.get(self.url).content)))
        print("당신은 이 이미지를 {}번 보았습니다".format(self.n))
        self.n = self.n+1 
k4=Klass4()
k4.show()
당신은 이 이미지를 3번 보았습니다

- 위의 예제는 아래와 같이 구현할 수도 있다.

class Klass4:
    n = 1
    url = 'https://github.com/guebin/IP2022/blob/master/_notebooks/2022-05-07-stop1.jpeg?raw=true'
    def show(self):
        display(Image.open(Image.io.BytesIO(requests.get(self.url).content)))
        print("당신은 이 이미지를 {}번 보았습니다".format(self.n))
        #self.n = self.n+1 
k4 = Klass4() 
k4.n
1
k4.show()
당신은 이 이미지를 1번 보았습니다
k4.n = k4.n + 1
k4.show()
당신은 이 이미지를 2번 보았습니다
k4.n = k4.n + 1 

- 결국에는 k4.n = k4.n + 1의 기능을 구현하여 넣은것이 self.n = self.n + 1 이다.

- 따라서 self는 k4에 대응한다. 즉 self는 인스턴스의 이름에 대응한다. 우리가 하고 싶은 것은 클래스를 선언하는 시점에서 인스턴스가 생성된 이후시점에 대한 어떠한 동작들을 정의하고 싶다. 그런데 클래스를 설계하는 시점에서는 인스턴스의 이름이 정해지지 않았으므로 (아직 인스턴스가 태어나지도 않음) 이러한 동작들을 정의하기 불편하다. 그래서 클래스를 설계하는 시점에서 그 클래스로부터 만들어지는 인스턴스는 그냥 self라는 가칭으로 부른다. (굳이 비유하면 self는 인스턴스의 태명같은 것임)

- 요약: self의 의미는 (후에 만들어질) 인스턴스의 이름이다. (즉 self는 인스턴스의 태명같은것임)

파이썬의 비밀1

탐구: 인스턴스의 자료형이 뭔지 탐구해보자!

- 아래의 두 클래스를 선언해보자.

class STOOOP: 
    #title = '학교폭력!' 
    url = url1
    end = '멈춰~~~~'
    def __init__(self,title=None): 
        self.title = title
    def stop(self):
        print(self.title)
        display(Image.open(Image.io.BytesIO(requests.get(self.url).content)))
        print(self.end) 
class Klass4:
    n = 1
    url = 'https://github.com/guebin/IP2022/blob/master/_notebooks/2022-05-07-stop1.jpeg?raw=true'
    def show(self):
        display(Image.open(Image.io.BytesIO(requests.get(self.url).content)))
        print("당신은 이 이미지를 {}번 보았습니다".format(self.n))
        #self.n = self.n+1     

- 인스턴스를 생성해보자.

k4=Klass4()
s1=STOOOP() 

- 타입을 알아보자.

k4?
Type:        Klass4
String form: <__main__.Klass4 object at 0x7f0a740beb80>
Docstring:   <no docstring>
s1?
Type:        STOOOP
String form: <__main__.STOOOP object at 0x7f0a74372970>
Docstring:   <no docstring>

- ??? 타입은 자료형, 즉 int, float, list 이런것 아니었나??

a=[1,2,3]
a?
Type:        list
String form: [1, 2, 3]
Length:      3
Docstring:  
Built-in mutable sequence.

If no argument is given, the constructor creates a new empty list.
The argument must be an iterable if specified.

- 그런데 지금 k4, s1의 타입은 Klass4, STOOOP 이다.

  • 가설1: 사실 파이썬 내부에 Klass4, STOOOP이라는 자료형이 있었다. 그런데 내가 만든 k4, s1이 우연히 그 자료형을 따르는 것! (이건 너무 억지스럽다)
  • 가설2: type이 list인것은 사실 list라는 클래스에서 생긴 인스턴스이다 -> 리스트자료형을 찍어낼 수 있는 어떠한 클래스가 파이썬에 내부적으로 존재할 것이다. (이게 맞는것 같다)

깨달음1

- 가설2가 맞다? 그렇다면 아래는 모두 어딘가에서 찍혀진 인스턴스이다.

a=[1,2,3]
a?
Type:        list
String form: [1, 2, 3]
Length:      3
Docstring:  
Built-in mutable sequence.

If no argument is given, the constructor creates a new empty list.
The argument must be an iterable if specified.
a=1,2,3
a?
Type:        tuple
String form: (1, 2, 3)
Length:      3
Docstring:  
Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.
a=1
a?
Type:        int
String form: 1
Docstring:  
int([x]) -> integer
int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments
are given.  If x is a number, return x.__int__().  For floating point
numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string,
bytes, or bytearray instance representing an integer literal in the
given base.  The literal can be preceded by '+' or '-' and be surrounded
by whitespace.  The base defaults to 10.  Valid bases are 0 and 2-36.
Base 0 means to interpret the base from the string as an integer literal.
>>> int('0b100', base=0)
4
a='1'
a?
Type:        str
String form: 1
Length:      1
Docstring:  
str(object='') -> str
str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or
errors is specified, then the object must expose a data buffer
that will be decoded using the given encoding and error handler.
Otherwise, returns the result of object.__str__() (if defined)
or repr(object).
encoding defaults to sys.getdefaultencoding().
errors defaults to 'strict'.

- 그리고 위의 a=[1,2,3]과 같은 것들은 모두 "클래스->인스턴스"에 해당하는 과정이었음

깨달음2

- 생각해보니까 아래와 같이 list를 선언하는 방식도 있었음

a=list()
a
[]
  • 이거 지금 생각하니까 list라는 이름의 클래스에서 a라는 인스턴스를 찍어내는 문법이다?!

- 아래도 가능함

a=list((1,2,3))
a?
Type:        list
String form: [1, 2, 3]
Length:      3
Docstring:  
Built-in mutable sequence.

If no argument is given, the constructor creates a new empty list.
The argument must be an iterable if specified.
  • 이것도 지금 보니까 list라는 이름의 클래스에서 a라는 인스턴스를 찍어내는 문법이다. 여기에서 (1,2,3)은 __init__()의 입력이다.

깨달음3

- 그러고보니까 각 자료형마다 특수한 기능들이 있었음

a=[1,2,3] 

- a.+tab 을 하면 append, clear 등등이 나온다.

- 이러한 기능은 지금까지 우리가 "list자료형 특수기능들"이라고 부르면서 사용했었다. 그런데 a가 list클래스에서 생성된 인스턴스라는 관점에서 보면 이러한 기능들은 list클래스에서 정의된 메소드라고 볼 수 있다.

깨달음4

- a.f()f(a)로 해석가능하다고 하였다. 이 해석에 따르면 메소드의 첫번째 입력은 메소드가 소속된 인스턴스라고 해석할 수 있다.

- 동일한 논리로 아래의 코드는 stop()의 입력에서 s1을 넣는다는 의미이다.

s1.stop()
None
멈춰~~~~

숙제

아래의 조건에 맞는 클래스를 생성하라.

(1) ['가위','바위']와 같은 리스트를 입력으로 받아 인스턴스를 생성한다.

(2) 위의 리스트에서 하나의 값을 뽑는 메소드 f를 가지고 있다.

사용예시

a = Klass(['가위','바위'])
a.f() # 가위가 1/2 바위가 1/2의 확률로 출력 
b = Klass(['가위','바위','보'])
b.f() # 가위, 바위, 보가 1/3의 확률로 출력

5월16일에 1시간, 5월18일에 2시간 분량을 업로드하겠습니다. (원래 5월16일 2시간, 5월18일 1시간 분량이었습니다)