python class - staticmethod, classmethod

|

패스트캠퍼스 웹 프로그래밍 수업을 듣고 중요한 내용을 정리했습니다.
개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
이 포스팅에서는 python 클래스에 대해 설명합니다.

staticmethod classmethod


staticmethod (정적 메서드)

스태틱 메서드는 클래스 내부에 정의된 일반 함수이며, 단지 클래스나 인스턴스를 통해 접근할 수 있을 뿐, 해당 클래스나 인스턴스에 영향을 주는 것은 불가능하다.

스태틱 메서드는 다양한 방식으로 인스턴스를 생성하는 클래스를 작성할 때 주로 사용된다.

__init__()초기화 함수는 하나만 존재할 수 있기때문에, 다른 생성함수를 정적 메서드로 만들어 사용할 수 있다.

import random

class Pokemon:
  def __init__(self, name, type):
    self.name = name
    self.type = type

  @staticmethod
  def electric():
    pokemons = ('피카츄', '라이츄', '파이리')
    selected_pokemon = random.choice(pokemons)
    return Pokemon(selected_pokemon, '전기')

  @staticmethod
  def water():
    pokemons = ('꼬부기', '아쿠스타', '라프라스')
    selected_pokemon = random.choice(pokemons)
    return Pokemon(selected_pokemon, '물')

classmethod (클래스 메서드)

클래스 메서드는 클래스 속성에 대해 동작하는 메서드이다. 일반적인 인스턴스 메서드와 달리 호출 주체가 클래스이며, 첫번째 인자도 클래스이다.

만약 인스턴스가 첫번째 인자로 주어지더라도 해당 인자의 클래스로 자동으로 바뀌어 전달된다.

클래스 메서드를 사용하면 부모 클래스를 상속받은 자식 클래스에서 해당 부모 클래스의 클래스 메서드를 사용할 경우 자기 자신(자식)의 클래스를 사용할 수 있다.

class Shop:
  description = 'Shop class'
  def __init__(self, name):
    self.name = name

  @classmethod
  def change_description(cls, desc):
    cls.description = desc

python class

|

패스트캠퍼스 웹 프로그래밍 수업을 듣고 중요한 내용을 정리했습니다.
개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
이 포스팅에서는 python 클래스에 대해 설명합니다.


class

객체지향 프로그래밍

파이썬의 모든 것은 객체이며, 객체를 사용할 때는 변수에 해당 객체를 참조(Reference)시켜 사용한다.

객체는 변수와 함수를 가지며, 특별히 객체 가 가진 변수함수속성(attribute)메서드(method)라고 부른다.

이러한 객체는 어떠한 타입, 즉 특정한 클래스 의 형태를 가진 인스턴스 를 나타낸다.

즉, 클래스란 똑같은 무엇인가를 계속해서 만들어 낼 수 있는 설계 도면 같은 것이고,
객체란 클래스에 의해서 만들어진 피조물을 뜻한다.

이러한 클래스에 의해서 만들어진 객체에는 중요한 특징이 있는데, 그것은 객체별로 독립적인 성격을 갖는다는 것이다.  
(독립된 단위가 알아서 움직이도록 하는 것)

class Cookie:
  pass

a = Cookie()
b = Cookie()

라고 했을 때, Cookie()의 결과값을 받은 와 b는 객체이다.
그리고 일반적으로 객체와 인스턴스는 같은 맥락에서 사용한다.

init 초기화 메서드

객체를 생성할 때, 인자를 어떻게 전달받고, 받은 인자를 이용해 어떤 객체를 생성할 지 정의 내가 원하는 애가 무엇인지를 확인하기 위해서는 self가 필요하다.

class Shop:
  def __init__(self, name):
    self.name = name

객체의 메서드를 정의할 때, 첫번째 인수는 항상 self이다. self에는 메서드를 호출하는 객체 자신이 자동으로 전달된다.

이렇게 self가 자동으로 호출되는 이유는 클래스의 메서드를 사용할 때 어떤 객체가 해당 메서드를 사용하고 있는 지 알 수 있도록 하기 위해서 이다. 또한 이렇게 하나의 메서드를 여러 객체가 공유할 수 있다.

속성이나 메서드에 접근할 때는 객체.속성명, 객체.메서드명을 사용한다.

문제1

Shop 클래스에 show_info메서드를 정의하고 해당메서드는 상점정보 (<상점명>)을 출력한다.

class Shop:
  def __init__(self, name):
    self.name = name

  def show_info(self):
    print('상점정보 ({})'.format(self.name))

클래스 속성

어떤 하나의 클래스로부터 생성된 객체들이 같은 값을 가지게 하고 싶은 경우, 클래스 속성(class attribute)를 사용한다.

class Shop:
  description = 'Python Shop Class'
  def __init__(self, name):
    self.name = name

  def show_info(self):
    print('상점정보 ({})'.format(self.name))

마찬가지로, 객체들에게서 각각의 인스턴스와는 밸개의 공통된 메서드를 사용하게 하고 싶을 경우, 클래스 메서드(class method)를 사용한다.

메서드

인스턴스 메서드

인스턴스 메서드는 첫번째 인수로 self를 가진다. 인스턴스를 이용해 메서드를 호출할때, 호출한 인스턴스가 자동으로 전달되며, 전달받은 인스턴스는 상태를 확인하거나 조작하는 데에 사용된다.

문제2

Shop클래스의 초기화 메서드 인자로 shop_typeaddress를 추가하고, 해당 인자들을 사용해 객체의 초기값을 만들어준다.

Shop클래스의 인스턴스 메서드 show_info를 아래와 같은 결과를 출력할 수 있도록 수정해본다.

class Shop:
    def __init__(self, name, shop_type, address):
        self.name = name
        self.shop_type = shop_type
        self.address = address

    def show_info(self):
        print('상점정보 ({})'.format(self.name))
        print('유형: {}'.format(self.shop_type))
        print('주소: {}'.format(self.address))  

문제3

Shop클래스에 change_type인스턴스 메서드를 추가하고, 상점유형(shop_type)을 변경할 수 있는 기능을 추가한다.

새로운 Shop인스턴스를 하나 생성하고, show_info() 인스턴스 메서드를 사용해 본 후 change_type메서드를 사용해 shop_type을 변경시키고 다시 show_info()메서드를 실행해 결과가 잘 반영되었는지 확인한다.

class Shop:
    def __init__(self, name, shop_type, address):
        self.name = name
        self.shop_type = shop_type
        self.address = address

    def show_info(self):
        print('상점정보 ({})'.format(self.name))
        print('유형: {}'.format(self.shop_type))
        print('주소: {}'.format(self.address))  

    def change_type(self new_shop_type,):
        self.shop_type = new_shop_type

속성 접근 지정자

캡슐화

객체를 구현할 때, 사용자가 반드시 알아야할 데이터나 메서드를 제외한 부분을 은닉시켜 정해진 방법을 통해서만 객체를 조작할 수 있도록 하는 방식

객체의 데이터나 메서드의 은닉 정도를 결정할 때, 속성 접근 지정자를 사용한다.

  • change_type메서드나 change_description클래스 메서드를 사용하지 않고도 내부 내용을 변경 할 수 있다.

속성 이름을 __로 시작하면, 외부에서의 접근을 제한한다. 이 경우를 private지정자라고 한다.

파이썬은 속성을 실제로 사용하지 못하도록 숨기지 않고, 네임 맹글이라는 기법을 사용한다. 파이썬에서는 문법적으로 private데이터에 대한 접근을 막는 법을 제공하지 않는다. 이는 개발자에게 최대한 제약을 가하지 않는다는 파이썬의 철학때문이다.

get/set속성값과 property

파이썬에서는 지원하지 않지만, 어떤 언어들은 외부에서 접근할 수 없는 private객체 속성을 지원한다. 이 경우, 객체에서는 해당 속성을 읽고 쓰기 위해 getter, setter메서드를 사용해야 한다.

파이썬에서는 해당 기능을 프로퍼티(property)를 사용해 간단히 구현한다.

class Shop:
    def __init__(self, name, shop_type, address):
        self.name = name
        self.shop_type = shop_type
        self.address = address

    def show_info(self):
        print('상점정보 ({})'.format(self.name))
        print('유형: {}'.format(self.shop_type))
        print('주소: {}'.format(self.address))  

    def change_type(self, new_shop_type):
        self.shop_type = new_shop_type

    @property
    def shop_type(self):
        return self.__shop_type

    @shop_type.setter
    def shop_type(self, new_shop_type):
        self.__shop_type = new_shop_type
        print('Set new shop type ({})'.format(self.__shop_type))

setter프로퍼티를 명시하지 않으면, 읽기전용이 되어 외부에서 조작할 수 없게 된다.

상속

거의 비슷한 기능을 수행하나, 약간의 추가적인 기능이 필요한 다른 클래스가 필요할 경우 기존의 클래스를 상속받은 새 클래스를 사용하는 형태로 문제를 해결할 수 있다.

이때, 상속 되는 클래스를 부모(상위)클래스라고 하며, 상속을 받는 클래스는 자식(하위)클래스라고 한다.

상속을 받을때는 클래스의 정의 다음 괄오에 부모클래스를 적어주면 된다.

class Restaurant(Shop):
  pass

상속받은 클래스는 부모 클래스의 모든 속성과 메서드를 사용할 수 있다.

메서드 오버라이드

상속받은 클래스에서, 부모클래스의 메서드와는 다른 동작을 하도록 할 수 있다. 이 경우 부모 클래스의 메서드를 덮어씌워서 사용하도록 하며, 이 방법을 메서드 오버라이드라고 한다.

부모 클래스의 메서드 호출 (super)

자식 클래스의 메서드에서 부모 클래스에서 사용하는 메서드의 전체를 새로 쓰는 것이 아닌, 부모 클래스의 메서드를 호출 후 해당 내용으로 새로운 작업을 해야할 경우 super()메서드를 사용해서 부모 클래스의 메서드를 직접 호출할 수 있다.

class Restaurant(Shop):
  def __init__(self, name, shop_type, address, rating):
    super().__init(self, name, shop_type, address):
    self.rating = rating

python module과 package

|

패스트캠퍼스 웹 프로그래밍 수업을 듣고 중요한 내용을 정리했습니다.
개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
이 포스팅에서는 python 모듈과 패키지에 대해 설명합니다.


module?

지금까지 셸에서 진행한 실습의 경우, 하나의 모듈 내부에서 작업한 것으로 취급된다.

파이썬 파일은 각각 하나의 모듈로 취급되며, 실행이나 함수의 정의, 단순 변수의 모음 등등 여러 역할을 한다.

모듈 불러오기 (import)

기본 모듈에서, 부가적인 기능들을 불러와 사용하는 형태

# python -> projects -> module이라는 폴더를 만들어
# module 내 vim을 통해 shop.py game.py lol.py 를 만든다.

# shop.py 내에는 buy_item이라는 함수를 만들어 실행시키도록 하고
def buy_item():
  print('Buy item!')

buy_item()

# game.py 내에는 play_game이라는 함수를 만들어 실행시키도록 한다.
def play_game():
  print('Play game!')

play_game()

# 그리고 lol.py 내부에는 shop.py와 game.py를 호출 시킨뒤 실행시키게 한다.
import shop
import play

print(' = Turn on game')
shop.buy_item()
game.play_game()
# 그러고 나서 셸에서 lol.py를 실행시키면
Play game!
Buy item!
=Turn on game
Play game!
Buy item!

이렇게 출력이 되는데, 그 이유는 lol.py에서 import를 하는 순간 shop.py와 game.py에 있는 함수가 각각 출력이 되는 것이다. (그렇기 때문에 중복으로 출력이 되는 것)

이를 막아야 하는데, 우리가 사실 모듈을 import하는 경우가 상당히 많은데, 이 모듈이 독자적인 행동을 갖고 싶은 경우가 많다.

우리가 일반적으로 셸에서

python shop.py
>>> Buy item!

python game.py
>>> Play game!

이렇게 나오는데, import를 하는 경우에는 이렇게 나오지 않게 하고 싶은 것

즉, 우리가 shop.py 혹은 game.py를 메인 프로그램으로 사용하는 경우에는 출력이 되지만(buy_item()와 play_game()이 실행되는 것), 단순 import를 하는 경우에는 그 안에 있는 함수만 가져오고 싶은 것

그럴 때 사용하는 것이 __name__이라는 변수이다.

name

파이썬 인터프리터를 이용해 실행한 코드인지를 확인하여 단순히 import한 모듈의 경우 실행을 막는 방식으로 각 모듈은 자신의 이르믈 가지며, 모듈 이름은 모듈의 전역변수 __name__에서 확인할 수 있다.

파이썬 인터프리터가 실행한 모듈의 경우, __main__이라는 이름을 가진다.

따라서 python <파일명>으로 실행한 경우에만 동작할 부분은 if문으로 감싸준다. 즉, 지금 자신, 이 모듈의 이름이 메인이라면 그 파일을 실행했구나라는 것을 파이썬이 알 수 있도록 해준다.

def buy_item():
  print('Buy item!')

if __name__ == '__main__':
  buy_item()


def play_game():
  print('Play game!')

if __name__ == '__main__':
  play_game()

# 이후 셸에서 lol.py를 해보면
=Turn on game
Play game!
Buy item!

그럼 이제까지는 lol.py를 실행시키면 바로 게임을 키고, 게임을 하고, 아이템을 구매하게 했는데, 이제는 그게 아닌 어떤 특정 입력을 기다렸다가 그 입력에 대해 대응하는 방법을 알아보도록 하겠다.

코드를 조금 바꿔보기

import game
import shop

def turn_on():
  print('= Turn on game')

  while True:
    choice = input('뭐할래? \n 1: 상점가기 \n 2: 게임시작하기 \n 3: 종료 \n 입력: ')

if __name__ == '__main__':
  turn_on()

그런데 이 방식을 하면 아무리 input에 값을 넣어도 실행이 되지 않는다. 따라서 각 wiled문에서 벗어날 수 있도록 input에 값을 만들어 준다.

import game
import shop

def turn_on():
  print('= Turn on game')

  while True:
    choice = input('뭐할래? \n 1: 상점가기 \n 2: 게임시작하기 \n 3: 종료 \n 입력: ')

    if choice == '1':
      shop.buy_item()
    elif choice == '2':
      game.play_game()
    elif choice == '3':
      break
    else:
      print('1,2,3 중 하나를 선택해주세요.')

if __name__ == '__main__':
  turn_on()

네임스페이스 (namespace)

각 모듈은 독립된 네임스페이스(이름 공간)을 가진다. 지난번에 클로져를 가진다고 까지는 이야기를 했는데, 근데 그 클로져를 사용하는 방법이 네임스페이스를 이용하는 방법이다.

메인으로 사용되고 있는 모듈이 아닌 import된 모듈의 경우, 해당 모듈의 네임 스페이스를 사용해 모듈 내부의 데이터에 접근한다.

즉, lol.py에서 사용한 import gamegame.py에 있는 play_game함수에 어떻게 접근하냐면, 이 namespaceimport gamegame이라는 이름을 갖는 것

그래서 game.play_game()을 실행시킬 수 있는 것이다.

만약 모듈명을 생략하고 싶다면 (game.play_game()이 아니라 play_game()이라고 하고 싶다면)

# import game
from game import play_game

# 현재 스코프, 즉 네임 프로그램에 있는 함수 이름이랑, 다른 데서 import할 때 shop에서도 뭔가 play_game이랑 똑같은 무언가를 가져온다면 밑에 재정의한걸로 덮어씌워지니까 조심해야한다.

import shop

def turn_on():
  print('= Turn on game')

  while True:
    choice = input('뭐할래? \n 1: 상점가기 \n 2: 게임시작하기 \n 3: 종료 \n 입력: ')

    if choice == '1':
      shop.buy_item()
    elif choice == '2':
      # 해당 네임스페이스를 사용하지 않아도 된다.
      play_game()
    elif choice == '3':
      break
    else:
      print('1,2,3 중 하나를 선택해주세요.')

if __name__ == '__main__':
  turn_on()

그러면 이제 각각 똑같은 이름의 함수를 만들어 본다.

def buy_item():
  print('Buy item!')

def shop_info()
  print('shop module')

if __name__ == '__main__':
  buy_item()


def play_game():
  print('Play game!')

def shop_info():
  print('game module')

if __name__ == '__main__':
  play_game()

# lol.py에서

# import game
from game import play_game,shop_info as game_info
from shop import shop_info as shop_info
import shop

game_info()
shop_info()

def turn_on():
  print('= Turn on game')

  while True:
    choice = input('뭐할래? \n 1: 상점가기 \n 2: 게임시작하기 \n 3: 종료 \n 입력: ')

    if choice == '1':
      shop.buy_item()
    elif choice == '2':
      # 해당 네임스페이스를 사용하지 않아도 된다.
      play_game()
    elif choice == '3':
      break
    else:
      print('1,2,3 중 하나를 선택해주세요.')

if __name__ == '__main__':
  turn_on()

game module
shop module
=Turn on game
뭐할래?
 1: 상점가기
 2: 게임시작하기
 3: 종료
 입력:

패키지

특별한 폴더를 뜻하는데, 지금까지 만든 모듈은 다 같은 라인에 있는데, 패키지로 만들게 되면 계층구조를 가질 수 있다. 모듈들을 하나의 패키지안에 몰아넣고 나중에 어떤 기능들을 하나의 패키지들을 몰아넣고 그 패키지에서 기능들을 가져다 쓸 수 있도록 한다.

패키지를 만들때는 패키지로 사용할 폴더에 __init__.py파일이 있어야한다.

# 우리는 function이라는 파일을 만들어서 game.py와 shop.py를 해당 폴더에 이동하고
# 그 폴더 안에 __init__.py를 만든다. (touch __init__.py)

# 그러고 나서 lol.py를 실행하면 분명 module not found 에러가 뜬다.
# 그건 아래처럼 고치면 된다.

# import game
from function.game import play_game,shop_info as game_info
from function.shop import shop_info as shop_info
from function import shop

game_info()
shop_info()

def turn_on():
  print('= Turn on game')

  while True:
    choice = input('뭐할래? \n 1: 상점가기 \n 2: 게임시작하기 \n 3: 종료 \n 입력: ')

    if choice == '1':
      shop.buy_item()
    elif choice == '2':
      # 해당 네임스페이스를 사용하지 않아도 된다.
      play_game()
    elif choice == '3':
      break
    else:
      print('1,2,3 중 하나를 선택해주세요.')

if __name__ == '__main__':
  turn_on()

all

패키지에 포함된 하위 패키지 및 모듈을 모두 불러올 때, *을 사용하면 모듈의 모든 식별자들을 불러온다. 이때, 각 모듈에서 자신이 import될 때 불러와질 목록을 지정하고자 한다면 __all__을 정의하면 된다.

패키지 자체를 import시에 자동으로 가져오고 싶은 목록이 있다면, 패키지의 __init__.py파일에 해당 항목을 import해주면 된다.

from function.game import *

*을 해서 가져오되, 주지 않아도 될 것들이 있을 때 (쓸데 없는 import를 막기 위해), 내가 지정한 것만 import 하고 싶다면 __all__을 사용한다.

__all__ = (
  'play_game',
  'shop_info',
  )

이렇게 되면 all안에 있는 play_gameshop_info함수만 가지고 오는 것을 의미한다.

python 함수 실습문제 정리

|

패스트캠퍼스 웹 프로그래밍 수업을 듣고 중요한 내용을 정리했습니다.
개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.
이 포스팅에서는 python 함수 실습문제를 다시 정리해봅니다.


1.매개변수로 문자열을 받고, 해당 문자열이 red면 apple을, yellow면 banana를, green이면 melon을, 어떤 경우도 아닐 경우 I don’t know를 리턴하는 함수를 정의하고, 사용하여 result변수에 결과를 할당하고 print해본다.

fruit_dict = {
  'red':'apple',
  'yellow':'banana',
  'green':'melon',
}

def print_fruit_name(color):
  return fruit_dict.get(color, 'i don\'t know')

2.1번에서 작성한 함수에 docstring을 작성하여 함수에 대한 설명을 달아보고, help(함수명)으로 해당 설명을 출력해본다.

fruit_dict = {
  'red':'apple',
  'yellow':'banana',
  'green':'melon',
}

def print_fruit_name(color):
  '이 함수는 색깔을 물어보면 해당 과일의 이름을 출력해줍니다.'
  return fruit_dict.get(color, 'i don\'t know')


>>> help(print_fruit_name)

3.한 개 또는 두 개의 숫자 인자를 전달받아, 하나가 오면 제곱, 두개를 받으면 두 수의 곱을 반환해주는 함수를 정의하고 사용해본다.

def square_or_multi(a, b=None):
  if b:
    return a*b
  else:
    return a**2

# 더 간단한 방식으로 (이중제어문)
def square_or_multi(a, b=None):
  return a*b if b else a**2

# 혹은
def square_or_multi(*args):
  if len(args) == 2:
    return args[0] * args[1]
  else:
    return args[0] ** 2

# 더 간단하게
def square_or_multi(*args):
  if len(args) == 2:
    return args[0] * args[1]
  return args[0]** 2

4.두 개의 숫자를 인자로 받아 합과 차를 튜플을 이용해 동시에 반환하는 함수를 정의하고 사용해본다.

def plus_and_minus(a,b):
  return (a+b, a-b)

# 둘다 모두 양수로 출력하고 싶다면
def plus_and_minus(a,b):
  if a > b :
    sub = a-b
  else:
    sub = b-a

  return (a+b, sub)

# 이중 제어문을 사용하면
def plus_and_minus(a,b):
  return (a+b, a-b if a>b else b-a)

5.위치인자 묶음을 매개변수로 가지며, 위치인자가 몇 개 전달되었는지를 print하고 개수를 리턴해주는 함수를 정의하고 사용해본다.

def print_args(*args):
  print('args의 갯수: {}개'.format(len(args)))
  return len(args)

6.람다함수와 리스트 컴프리헨션을 사용해 한 줄로 구구단의 결과를 갖는 리스트를 생성해본다.

[(lambda a,b : '{} x {} = {}'.format(a,b, a*b)) (x,y) for x in range(2,10) for y in range(1,10)]

python 모르는 부분 따로 정리

|

패스트캠퍼스 웹 프로그래밍 수업을 듣고 중요한 내용을 정리했습니다.
개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


index 함수

한 리스트 내에서 내가 원하는 valuew의 위치를 알고 싶을 때 사용하는 함수

기본 사용법

A = [0,1,2,3]

target = 1
A.index(target)
>>> 1

=> index()는 찾는 값의 첫번째 위치를 반환한다

enumerate

enumerate는 열거하다라는 뜻으로 순서가 있는 자료형(리스트, 튜플, 문자열)을 입력으로 받아 인덱스 값을 포함하는 enumerate 객체를 리턴한다.

(보통 for문과 자주 사용된다.)

for i, name in enumerate(['body', 'foo', 'bar']):
... print(i, name)
...
>>> 0 body
    1 foo
    2 bar

순서값과 함께, body, foo, bar가 순서대로 출력되었다. 즉, 위 예제와 같이 enumerate를 for문과 함께 사용하면 자료형의 현재 순서(index)와 그 값을 쉽게 알 수 있다.

for 문처럼 반복되는 구간에서 객체가 현재 어느 위치에 있는 지 알려주는 인덱스 값이 필요할 때 사용하면 매우 유용하다.

dictinary key로 value얻기 (get)

>>> a = {'name':'pey', 'phone':'0119993323', 'birth': '1118'}
>>> a.get('name')
'pey'
>>> a.get('phone')
'0119993323'

get(x) 함수는 x라는 key에 대응되는 value를 돌려준다. 앞서 살펴보았듯이 a.get(‘name’)은 a[‘name’]을 사용했을 때와 동일한 결과값을 돌려받는다.

다만, 다음 예제에서 볼 수 있듯이 a[‘nokey’]처럼 존재하지 않는 키(nokey)로 값을 가져오려고 할 경우 a[‘nokey’]는 Key 오류를 발생시키고 a.get(‘nokey’)는 None을 리턴한다는 차이가 있다. 어떤것을 사용할지는 여러분의 선택이다.