파이썬 문제

김병욱·2020년 4월 23일
0

Python

목록 보기
12/14

클래스 문제

Assignment
Class 를 직접 구현해 보겠습니다.
Database 라는 이름의 class를 구현해 주세요.
Database 클래스는 다음의 속성(attribute)들을 가지고 있습니다.

name : database의 이름
size : 저장할 수 있는 데이터의 max 사이즈. Size를 넘어서는 데이터를 저장할 수 없다.

Database 클래스는 다음의 메소드들을 가지고 있습니다.

insert
select
update
delete

Insert
insert 메소드는 self 외에 2개의 parameter를 받습니다.  
field와 value 입니다. 
Field 는 저장하고자 하는 데이터의 필드명 이고 value는 값입니다. 
Field 와 value는 내부적으로 dictionary에 저장되어야 합니다.
insert 메소드는 다음 처럼 호출 할 수 있습니다. 

# 객체 이름이 db 라는 가정하에
db.insert("name", "정우성")

insert 메소드는 특별한 리턴값은 없습니다.
만일 내부 dictionary의 총 사이즈가 database 클래스의 size 속성보다 크면 더이상 새로운 값들을 저장하지 말아야 합니다.


Select
select 메소드는 self 외에 1개의 parameter를 받습니다. 
field 입니다.
Field 는 읽고자 하는 데이터의 필드명 입니다.
내부적으로 데이터를 저장하고 있는 dictionary에서 해당 field에 해당하는 키와 연결되어 있는 값을 리턴해주어야 합니다.

예를 들어,  이미 name이라는 필드명으로 "정우성" 이라는 값을 저장했다고 한다면:

# 객체 이름이 db 라는 가정하에
db.select("name")
> "정우성"

이 되어야 합니다.  
만일 해당 필드값으로 저정되어 있는 값이 없다면 None 을 리턴해주세요.

Update
Self 외에 2개의 parameter를 받습니다.  
field와 value 입니다. 
이름 그대로 이미 저장되어 있는 값을 수정하는 메소드 입니다.

# 객체 이름이 db 라는 가정하에
db.update("name", "아이유")

만일 field값에 해당하는 데이터가 저장되어 있지 않으면 아무것도 하지 않습니다.
그리고 특별한 리턴 값은 없습니다.

Delete
delete 메소드는 self 외에 1개의 parameter를 받습니다. 
field 입니다.
Field 는 지우고자 하는 데이터의 필드명 입니다.

# 객체 이름이 db 라는 가정하에
db.delete("name")

만일 field값에 해당하는 데이터가 저장되어 있지 않으면 아무것도 하지 않습니다.
그리고 특별한 리턴 값은 없습니다.

클래스 정답

class Database:

  def __init__(self,name,size):
    self.name = name
    self.size = size
    self.data = {}
    #data 라는 속성을 하나 더 부여해준다
    
  def insert(self,field,value):
    if len(self.data) >= self.size:
      print('값이 꽉참')
    else:
      self.data.update({field:value})
    
    
  def select(self,field):
    if field in self.data:
      return self.data[field]
      # dict.get(key)는 키를 통하여 값을 반환
    else:
      return None
  
  def update(self,field,value):
    keys = self.data.keys()
    #keys 변수에 dict.keys() 로 data속성의 키값들을 모두 가져온다
    if field in keys:
      self.data.update({field:value})
      #dict.update({키:값})은 새로운 키와 값을 추가하거나 수정한다
      return self.data
    else:
      print('그런값없음')
      
      
  def delete(self,field):
    keys = self.data.keys()
    if field in keys:
      print(self.data.pop(field))
      # dict.pop(key)는 키를 통해 값을 반환한 후 삭제한다
    else:
      print('그런값없음')
    
      
data = Database('kim',3)
data.insert('name','김병욱')
print(data.select('name'))
data.insert('age',27)
print(data.select('age'))

파이썬의 모듈과 패키지

Module

파이썬에서 모듈은 변수나 함수 그리고 클래스 등을 모아놓은 파일입니다.
따로 모듈로 모아놓은 이유는 다음과 같습니다.

다른 파일에서 재사용이 가능하게 하고
전체 코드가 한 파일에 넣기에는 너무 커졌을때 여러 파일로 나누어서 정리를 하기 위해서.

예를 들어, List의 총 요소 개수를 알고 싶으면 len 함수를 사용하면 되는걸 이미 보았습니다.

my_list = [1, 2, 3, 4, 5]
my_list_length = len(my_list)

print(my_list_length)

하지만 len 함수를 직접 구현한적은 없습니다.
파이썬에서 이미 모듈로 구현해놓았음으로 그냥 가져다 쓰면 되는 것입니다.
이렇게 모듈은 다른 사람들이 이미 만들어놓은 모듈을 사용할 수도 있고 우리가 직접 만들어서 사용할 수도 있습니다.

Module 만들기
Module을 만드는 것은 어렵지 않습니다.
단순히 파일을 만든후 그 안에 재사용 하고 싶은 함수나 클래스 혹은 변수등을 구현하면 됩니다.

my_module.py

my_module_var = 7

def my_module_func():
return "Hello!"

class MyModuleClass:
pass

그 후 단순히 다른 파일에서 불러와 사용하면 됩니다.
모듈을 불러와 (import) 사용하기 위해서는 import라는 키워드를 사용하면 됩니다.

import <모듈 이름>

주의할점은 모듈을 import 할때 파일 확장자인 ".py" 는 제외하고 파일 이름만 사용해서 import 해야 합니다.

import my_module

원하는 모듈을 import 한 후에는, 다음과 같이 모듈의 원하는 변수/함수/클래스를 사용할 수 있습니다.

<모듈 이름>.<모듈에서 사용하길 원하는 변수/함수/클래스 이름>

예를 들어, my_module 모듈의 my_module_func 함수를 호출하고 싶으면 다음 처럼 할 수 있습니다.

my_module.my_module_func()

이렇게 모듈 이름을 앞에 붙여줘야 하는 이유는, 그래야 파이썬이 어느 파일을 봐야 해당 함수를 찾을수 있는지 알 수 있기 때문입니다. 만일 모듈 이름을 안붙어주면 파이썬은 모듈의 파일을 보지 않고 해당 파일에서만 함수를 찾을려고 합니다 (그리고 못찾을거기 때문에 에러가 납니다). 이러한 구조를 "name space" 라고 합니다.
이름의 공간 혹은 영역을 알려주기 때문입니다.

main.py

import my_module

print(my_module.my_module_var)

my_module.my_module_func()

my_module_class = my_module.MyModuleClass()

다시 정리하면,

  1. 원하는 모듈을 import 키워드를 사용하여 불러온다. 그럼으로 파이썬이 모듈을 해당 파일과 연결시키게 된다.
  2. 그 후 연결된 모듈에서 원하는 변수/함수/클래스 를 사용하면 된다. 다만 해당 모듈 이름과 연결해서 호출을 해줘야 파이썬이 어느 파일에서 해당 변수/함수/클래스 를 찾을 수 있는지 알 수 있게된다.

Alternative ways to import modules
import 키워드 외에 from import 키워드를 사용해서 모듈을 불러들일수 있습니다.

from <모듈 이름> import <함수/변수/클래스1>, <함수/변수/클래스2>, ..., <함수/변수/클래스N>

예를 들어, my_module 모듈에서 my_module_func 함수와 my_module_var 변수를 import 하고자 한다면 다음처럼 할 수 있습니다.

from my_module import my_module_func, my_module_var

print(my_module_var)
my_module_func()

from import 키워드를 사용하여 모듈을 불러들이는 경우 모듈 이름을 붙이지 않고 곧바로 원하는 함수나 변수 그리고 클래스를 호출할 수 있습니다. 모듈에서 사용하는 것이 명확할때 from import 를 사용하면 편리합니다.

다음 처럼 별표( * )를 사용하면 해당 모듈의 모든 요소가 곧바로 import 됩니다.

from my_module import *

print(my_module_var)
my_module_func()

하지만 이렇게 하는 것은 권장되지 않습니다. 왜냐하면 local scope를 가지고 있는 다른 변수/함수/클래스 들과 이름 충돌이 날 수 있는데, 만일 이름 충돌이 일어났을 경우 알기가 쉽지 않을수 있기 때문입니다.

Import As
만일 여러 모듈을 import 하게 되면 이름 충돌이 날 수도 있습니다. 예를 들어 서로 다른 모듈에서 동일한 이름의 함수가 있을 수도 있는것입니다. 혹은 원하는 모듈의 요소의 이름이 너무 길수도 있습니다. 이러한 경우 import as 키워드를 사용해서 새로운 이름을 주어서 사용할 수 있습니다.

from my_module import my_func as f1
from my_module2 import my_func as f2
from my_module3 import function_with_name_too_long as f3

f1()
f2()
f3()

module 이름도 as 를 사용하여 새로운 이름을 줄 수 있습니다.

import my_module as m1

m1.my_module_func()

Packages

Package는 module과 마찬가지로 다른 파일에서 불러와서 사용하는 개념인데요, 차이점은 module보다 더 크고 복잡한 코드라는 점입니다. 앞서 보았듯이 module은 단순한 파이썬 파일입니다.
그러나 어떠한 module들은 코드의 양이 너무 커서 한 파일에 다 넣기에 비효율적일수 있습니다.
그러할 경우 여러 파일에 나누어서 코드를 관리하는것이 효과적일 것입니다.
이렇게 여러 파일에 나누어져 있는 코드들도 다른 곳에서 하나의 module로 불러와서 사용할 수 있도록 해주는것이 package 입니다.

Package는 파이썬 파일들로 이루어져 있는 디렉토리(directory)가 하나의 package가 됩니다. 그리고 디렉토리 이름이 package 이름이 됩니다.

Package는 일반 모듈 처럼 import 하여 사용할 수 있습니다. 다만 차이점은 클래스 객체를 사용할때 처럼 "dot notation" 으로 해당 package의 원하는 모듈을 import 하면 됩니다.

import pkg.mod1
from pkg.mod2 import func2

pkg.mod1.func2()
func2()

Package Initialization
가끔은 package가 import 될때 초기 설정을 해줘야 할때가 있습니다.
파이썬은 init.py 파일을 통해 package 초기 설정을 가능하게 해줍니다.

Package 안에 init.py 파일이 있으면 package가 import 될때 init.py 파일의 코드들이 자동으로 실행됩니다. 그렇다면 init.py 파일을 통하여 정확히 무엇을 할 수 있는걸까요?
일반적으로 다음 사항들을 할 수 있습니다.

Import 할때 경로의 총 길이 줄여주기
Package에서 import 할 수 있는 변수/함수/클래스 제한하기
그 외 package가 import될때 꼭 먼저 실행되어야 하는 코드들

먼저 import 할때 경로의 총 길이 줄여주기에 대해서 보겠습니다.
현재 pkg에서 mod1의 func2 라는 함수를 import 하여 사용하기 위해서는 다음과 같이 해야합니다.

import pkg.mod1

pkg.mod1.func2()

func2 함수를 호출 할때마다 매번 모든 경로를 다 타입해줘야 하기때문에 번거롭습니다.
함수 이름을 곧바로 호출 할 수 있게 하면 편할것 같습니다. 다행히도 init.py 파일을 통해 함수의 경로를 줄여줄 수 있습니다. 방법은 아주 간단합니다. init.py 파일에 먼저 한번 import 해주면됩니다.

init.py

from .mod1 import func2

main.py

from pkg import func2

func2()

init.py 파일을 사용해서 import 할 수 있는 변수/함수/클래스를 제한할 수 있습니다.
예를 들어 모듈의 모든 함수가 다 외부로 노출될 수 있는건 아닐수 있습니다. 내부적으로만 사용되어야 하는 함수도 있을수 있는데요, 이러한 함수가 package 외부에서 import되어 사용되는 것을 막기 위해서는 all 변수를 지정해 줄 수 있습니다.

package를 통해 import 될 수 있는 요소들은 모두 all 변수를 통해 정의 됩니다.
그리고 all 변수의 default 값은 모든 함수/변수/클래스 입니다.
그러므로 all 변수를 따로 정의해줌으로 import 될 수 있는 요소들을 제한할 수 있는 것입니다.
all 변수는 string 값의 요소를 가지고 있는 list 입니다 (list of strings).
그러므로 import 되길 원하는 요소들을 string으로 list에 선언해주면 됩니다.

init.py

from .mod1 import func2
from .mod2 import func3

all = ['func2', 'func3']

main.py

from pkg import *

func2()
func3()
func4() ## <== Error. func4 함수는 all 에 정의되지 않았으므로 import 될 수 없음.

다른 사람의 package 사용하기
다른 사람이 만들 package를 import 하여 사용하기 위해서는 먼저 package를 설치를 해야 합니다.
예를 들어, Django 라는 package를 사용하기 위해서는 먼저 설치를 해야합니다.
파이썬에서 package를 설치하는 일은 매우 간단합니다 (일반적으로. 간단하지 않은 package도 있습니다).
PIP 라는 툴을 사용하면 됩니다. PIP는 파이썬의 package manager 입니다. 파이썬을 설치하면 같이 설치가 되며 pip은 터미널에서 다음 명령어를 사용하면 됩니다. 이부분은 일단 알고만 계시면 됩니다. 프로젝트를 할때 많이 해볼것입니다.

pip install Django

파이썬의 모듈 찾는 순서

How import statement finds modules and packages

앞서 module과 package를 만들고 import 하는 법을 보았습니다.
근데 파이썬은 module과 package를 어떻게 찾을까요?
파이썬이 module과 package 검색하는 방법을 알아보도록 하겠습니다.
본 수업의 내용은 약간 어려울 수 있습니다. 그래도 꼭 이해햐셔야 앞으로 시스템을 구현하시는데 도움이 됩니다!

Import Search 순서
예를 들어, abc 라는 package가 있다고 가정해 보겠습니다. 그리고 abc package를 사용하려면 다음처럼 import 해야 합니다.

import abc

여기서 abc는 단순한 파이썬 파일(모듈일 경우) 이거나 파이썬 파일들을 담고 있는 디렉토리(package의 경우) 입니다. 그러므로 해당 파일이나 디렉토리가 어디있는지 파이썬이 찾을 수 있어야 import가 가능할것입니다.

그럼 파이썬은 모듈/package를 어떻게 찾을까요?
파이썬은 다음 3가지 장소를 순서대로 보면서 찾습니다.

  1. sys.modules
  2. built-in modules
  3. sys.path

sys.modules
파이썬이 모듈이나 package를 찾기위해 가장 먼저 확인하는 곳입니다.
sys.modules는 단순한 dictionary 입니다. 그리고 이미 import된 모듈과 package들을 저장하고 있습니다.
즉, 한번 import된 모듈과 package들은 파이썬이 또 다시 찾지 않아도 되도록 하는 기능을 가지고 있습니다.
그러므로 새로 import 하는 모듈은 sys.modules 에서 찾을 수 없습니다.

built-in modules
파이썬에서 제공하는 파이썬 공식 라이브러리들 입니다.
Built-in 모듈들은 이미 파이썬에 포함되어 나오므로 파이썬이 쉽게 찾을 수 있습니다.

sys.path
마지막으로 보는 장소가 바로 sys.path 입니다.
sys.path는 기본적으로 list이며 string 요소들을 가지고 있는 list 입니다.
각 string 요소들은 다음 처럼 경로를 나타냅니다:

['',
'/Users/song-eun-u/anaconda3/bin',
'/Users/song-eun-u/anaconda3/lib/python36.zip',
'/Users/song-eun-u/anaconda3/lib/python3.6',
'/Users/song-eun-u/anaconda3/lib/python3.6/lib-dynload',
'/Users/song-eun-u/anaconda3/lib/python3.6/site-packages',
'/Users/song-eun-u/anaconda3/lib/python3.6/site-packages/aeosa',
'/Users/song-eun-u/anaconda3/lib/python3.6/site-packages/IPython/extensions',
'/Users/song-eun-u/.ipython']

그러므로 파이썬은 list의 각 경로를 하나 하나 확인하면서 해당 경로에 import 하고자 하는 package가 위치해 있는지 확인합니다.

참고로 sys 는 파이썬에 포함되어 있는 모듈입니다. 그러므로 다음 처럼 sys 모듈을 import 해서 sys.modules와 sys.path 를 출력할수도 있고 수정 할 수 도 있습니다.

import sys

print(sys.path)
print(sys.modules)

정리를 하자면, 파이썬은 import 하고자 하는 모듈과 package를 찾을때에 먼저 sys.modules를 보고, 없으면 파이썬 built-in 모듈들을 확인 하고 마지막으로 sys.path에 지정되어 있는 경로들을 확인해서 찾습니다.
sys.path 에서도 못찾으면 ModuleNotFoundError 에러를 리턴합니다.

Absolute Path & Relative Path
파이썬의 built-in 모듈과 pip 을 통해 설치한 외부 모듈 및 package는 일반적으로 import 하는데 큰 문제가 되지 않습니다. Built-in 모듈은 당연히 잘 찾아지고, pip 으로 설치한 외무 모듈도 자동으로 site-packages 라는 디렉토리에 설치가 되는데, 이 site-packages 는 sys.path에 이미 포함되어 있기때문에 찾는데 문제가 없습니다.

문제는 직접 개발한 local package 입니다. 직접 개발한 local package를 import 할때는 해당 package의 위치에 맞게 import 경로를 잘 선언해야 합니다. Local package를 import 하는 경로에는 absolute path 와 relative path 가 있습니다.

Absolute path는 이름 그대로 절대 경로 입니다. 왜 절대 경로인가 하니, import를 하는 파일이나 경로에 상관없이 항상 경로가 동일하기 때문입니다.

다음과 같은 프로젝트를 예를 들어 보겠습니다:
└── my_app
├── main.py
├── package1
│ ├── module1.py
│ └── module2.py
└── package2
├── init.py
├── module3.py
├── module4.py
└── subpackage1
└── module5.py

my_app 이라는 프로젝트 이며 package1과 package2 라는 2개의 package를 가지고 있습니다.
그리고 package2는 subpackage2 라는 중첩 package를 가지고 있습니다.

Absolute path를 사용해 package1 과 package2를 import 하면 다음과 같습니다.

from package1 import module1
from package1.module2 import function1
from package2 import class1
from package2.subpackage1.module5 import function2

경로들의 시작점이 전부 "my_app" 프로젝트의 가장 최상위 디렉토리에서 시작하는것을 볼 수 있습니다.
예를 들어, subpackage1의 module5 모듈의 function2 함수를 import 하기 위해서는 다음 경로를 거치게 됩니다.

my_app => package2 => subpackage1 => module5.py

이걸 리눅스의 directory 경로 형식으로 바꾸면 다음처럼 표현 할 수 있습니다.

my_app/package2/subpackage1/module5.py

윈도우스 형식이라면 다음과 같습니다.

my_app\package2\subpackage1\module5.py

파이썬에서는 slash (/) 나 back slack() 대신에 dot (.) 을 사용해서 경로를 표현 합니다.

my_app.package2.subpackage1.module5.py

이미 my_app 프로젝트 안에 있으므로 my_app 은 생략됩니다. 그러므로 다음처럼 경로를 표현하게 되는 것입니다.

package2.subpackage1.module5.py

이걸 from import 키워드를 사용해 import 하게 되면 다음 처럼 되는 것입니다.

from package2.subpackage1.module5 import function2

my_app 프로젝트 내에서는 어느 파일, 어느 위치에서 import 하던지 경로가 항상 위와 같이 동일하게 되므로 absolute path 라고 하는 것입니다.

참고로 current directory 라고 하는 현재의 프로젝트 디렉토리는 default로 sys.path 에 포함되게 됩니다.
그러므로 absolute path는 current directory 로 부터 경로를 시작하게 되는것입니다.

일반적으로 local package를 import 할때는 absolute path를 사용하면 됩니다.
다만 absolute path를 사용하게 되면 한가지 단점이 있는데 바로 경로가 길어질 수 있다는 점입니다.
그래서 이러한 단점을 보완하기 위해서 relative path를 사용할 수 있습니다.

Relative path 는 absolute path와 다르게 프로젝트의 최상단 디렉토리를 기준으로 경로를 잡는게 아니라 import 하는 위치를 기준으로 경로를 정의합니다. 그래서 일반적으로 relative path는 local package 안에서 다른 local package를 import 할때 사용됩니다.

예를 들어, package2의 module3에서 package2의 class1과 package2의 하위 package인 subpackage1의 module5의 function2 함수를 import하려고 하면 다음 처럼 할 수 있습니다.

package2/module3.py

from . import class1
from .subpackage1.module5 import function2

여기서 dot(.)은 import가 선언되는 파일의 현재 위치를 이야기 합니다. 현재위치는 package2/module3.py 이므로 현재 위치에서부터 원하는 모듈의 경로만 선언해주면 되는 것입니다.

또한 dot 2개를 사용할 수도 있습니다. dot 2개(..) 는 현재위치에서 상위 디렉토리로 가는 경로입니다.

subpackage1/module5.py

from ..module4 import class4

Relative path는 선언해야 하는 경로의 길이를 줄여준다는 장점은 있지만 헷갈리기 쉽고 파일 위치가 변경되면 경로 위치도 변경되어야 하는 단점이 있습니다. 그러므로 웬만한 경우 absolute path를 사용하는게 권장 됩니다.

파이썬 sys, 모듈 찾는 순서, 절대경로, 상대경로

블로그에 작성하였다.

https://velog.io/@bungouk6829/Python-%EC%9D%98-%EB%AA%A8%EB%93%88%EA%B3%BC-%ED%8C%A8%ED%82%A4%EC%A7%80-sys

profile
개발스터디

0개의 댓글