파이썬은 모든 것이 객체(Object)이어서, 그 속성이 mutable(가변) 과 immutable(불변)으로 구분된다. 

 mutable - 변경되는 객체 (객체의 상태를 변경할 수 있음)
 immutable - 변경되지 않는 객체 (객체의 상태를 변경할 수 없음

 변경이 되는 mutable 객체의 종류는 list, set, dictionary 정도가 있고
 변경이 되지 않는 immutable 객체의 종류는 int, float, tuple, str, bool 이 있습니다.

 이러한 개념을 잘 이해해야하는 이유는 깊은복사와 얕은복사를 이해하는데 필수적이기 때문이다. 파이썬에서는 immutable 객체의 값이 같은 경우에 변수에 상관없이 동일한 곳을 참조합니다. mutable (값이 변경될 수 있는) 객체의 경우에는 모든 객체를 각각 생성해서 참조해 줍니다.

mutable(가변)

 

Mutable 데이터 타입

  • 사용자 정의 객체
  • list
  • dictionary
  • set
## List

>>> a = [1,2]         #a = [1,2]
>>> id(a)
1985209849032
>>> a.append(3)       #a = [1,2,3]
>>> id(a)
1985209849032



## Dict

>>> a = {1:'a'}       #a = {1:'a'}
>>> id(a)
1985209931096
>>> a[2] = 'b'        #a = {1:'a', 2:'b'}
>>> id(a)
1985209931096

 mutable에 속한 객체들은 값이 변경될 때 객체 값도 변경되는 것을 확인할 수 있다.

## List

>>> a = [1,2]
>>> b = a             #b = [1,2]
>>> a == b            #a = [1,2] , b = [1,2]
True
>>> b.append(3)       #b = [1,2,3]
>>> a == b            #a = [1,2,3] , b = [1,2,3]
True


## Dict

>>> a = {1:'a'}
>>> b = a             #b = {1:'a'}
>>> a == b            #a = {1:'a'} , b = {1:'a'}
True
>>> b[2] = 'b'        #b = {1:'a',2:'b'}
>>> a == b            #a = {1:'a',2:'b'} , b = {1:'a',2:'b'}
True

mutable 객체들은 값이 변경될 때 주소가 참조하는 값도 변경되는것을 확인할 수 있었다.

 

 

immutable(불변)

 

Immutable 데이터 타입

  • int
  • float
  • demical
  • bool
  • string
  • tuple
  • range
## Int

a = 1                 #1
>>> id(a)
140729439544144
>>> a += 1            #2
>>> id(a)
140729439544176

## String

a = 'a'               #a
>>> id(a)
1985199052704
>>> a += 'b'          #ab
>>> id(a)
1985209620944


## Tuple

>>> a = (1,2)         #(1,2)
>>> id(a)
1985209935624
>>> a += (3,)         #(1,2,3)
>>> id(a)
1985209858448

  immutable 에 속한 객체들은 값이 변경이될떄 객체가 변하는것을 확인 할 수 있습니다.  여기에 속한 객체들은 call by value 의 속성을 띄고있습니다.

## Int

>>> a = 1
>>> b = a             #b = 1
>>> a == b            #a = 1 , b = 1
True
>>> b += 1            #b = 2
>>> a == b            #a = 1 , b = 2
False


## String

>>> a = 'a'
>>> b = a             #b = 'a'
>>> a == b            #a = 'a' , b = 'a'
True
>>> b += 'b'          #b = 'ab'
>>> a == b            #a = 'a' , b = 'ab'
False


## Tuple

>>> a = (1,2)
>>> b = a             #b = (1,2)
>>> a == b            #a = (1,2) , b = (1,2)
True
>>> b += (3,)         #b = (1,2,3)
>>> a == b            #a = (1,2) , b = (1,2,3)
False

  immutable 객체들은 값이 변경되는것이 새로운 객체로생성이 되기떄문에 변경이 일어날떄 기존 객체는 변하지 않습니다.

'Python 프로그래밍' 카테고리의 다른 글

[Python] Future  (0) 2022.06.01
[Python] Object Reference  (0) 2022.05.27
[Programming] 암호화 알고리즘 (Encryption Algorithms)  (0) 2022.05.22
[Python(Web)] Robots.txt  (1) 2022.05.21
[Python] Decorator  (0) 2022.05.20

기본 용어 정의

  • 평문(Plaintext) : 해독 가능한 형태의 메시지(암호화전 메시지)
  • 암호문(Cipertext) : 해독 불가능한 형태의 메시지(암호화된 메시지
  • 암호화(Encryption) : 평문을 암호문으로 변환하는 과정
  • 복호화(Decryption) : 암호문을 평문으로 변환하는 과정
  • 전자서명
    • 송신자의 Private Key로 메시지를 서명하여 전달
    • 수신자측에서는 송신자의 Public Key를 이용하여 서명값을 검증
  • 양방향암호화 : 암호화와 복호화과정을 통해 송.수신간 주고받는 메시지를 안전하게 암.복호화하는 과정
  • 단방향암호화 : 해싱(Hashing)을 이용한 암호화 방식으로 양방향과는 다른 개념으로, 평문을 암호문으로 암호화는 가능하지만 암호문을 평문으로 복호화 하는 것은 불가능.

양방향 암호화(대칭/비대칭 Key)

  • 대칭키 : 같은 키를 이용하여 메시지를 암.복호화 하는 것
  • 비대칭키 : 메시지를 암호화 하는 키와 복호화 하는 키가 다름
    - 암호화 알고리즘에 따라 사용방식이 다를수도 있다
    - 전자서명을 위한 알고리즘에서는 Private Key로 메시지를 서명하고, Public Key로 검증
    - 메시지 교환에서는 Public Key로 메시지를 암호화하고 Private Key로 복호화한다.

1. 대칭키 암호화

  • 종류 : AES128, AES256, SEED(국내표준)
  • 암.복호화 키가 같음
  • 문제점은 수신측에 키를 전달하는 과정에서 유출될 우려가 있음
  • 스트림기반, 블록기반의 암호화로 나눌수 있음

1-1. 스트림기반 암호화

  • 비트단위로 암호화하는 방식.
  • LFFSR, MUX generator 등의 스트림기반 암호화 알고리즘
  • 속도가 빠르고 오류 전파 현상이 없다는 장점
  • 주로 오디오/비디오 스트리밍 시 사용

1-2. 블록기반 암호화

  • 블록 단위로 암호화를 수행하는 방식
  • 문자열 단어 하나하나를 블록으로 나누어 암호화하는 과정
  • DES, AES, IDEA, SEED 등의 블록기반 암호화 알고리즘

2. 비대칭키 암호화

  • 종류 : DSA(전자서명), RSA(메시지 암.복호화)
  • 대칭키에 비해서는 느리다는 단점이 있음
  • 키생성시 Private Key와 Public Key 2개의 키가 도출되며, Public Key는 공개해도 문제가 되지 않는다.
  • 인수분해, 이산대수, 타원곡선 암호화로 나뉨

2-1. ECC(Elliptic Curve Cryptography)

  • 타원곡선 암호화로써 RSA에 비해 짧은 길이의 키를 사용하면서도 비슷한 수준의 안정성을 제공
  • 비트코인 및 이더리움에서 ECC알고리즘을 이용

2-2. ECC 기반의 암호화

  • ECDSA(Elliptic Curve Digital Signature Algorithm)
    : 전자서명(ECC 암호화 알고리즘을 전자서명에 사용한 것)
  • ECDH(Elliptic Curve Diff-Hellman)
    : 키교환 알고리즘(자신의 Private Key와 상대방의 Public Key를 사용하여 공통된 Secret 키를 도출)
  • ECIES(Elliptic Curve Integreated Encryption Scheme)
    : 통합 암호화 방식(Public Key로 암호화하고 Private Key로 복호화)

 

단방향 암호화

  • Hash를 이용하여 암호화하는 과정
  • 평문을 암호화할순 있지만, 복호화는 불가능하다
  • 데이터의 진위여부는 확인하고 싶으나, 본 데이터의 Privacy를 지키고 싶은 경우 사용
  • ex) Bcrypt-JWT (참고:인증(Authentication) & 인가(Authorization))
  • hash할 때, hash 값은 크기와 알고리즘에 따라 암호문의 결과가 완전 상이함

단방향 Hash 알고리즘

알고리즘분류알고리즘명MD 길이블록 길이최대 메시지 길이

MD5 MD5 128bit 512bit 무한
RIPEMD RIPEMD-160 160bit 512bit 2^64-1bit
SHA-1 SHA-1 160bit 512bit 2^64-1bit
SHA-2 SHA-224 224bit 512bit 2^64-1bit
SHA-2 SHA-256 256bit 512bit 2^64-1bit
SHA-2 SHA-384 384bit 1024bit 2^128-1bit
SHA-2 SHA-512 512bit 1024bit 2^128-1bit
SHA-3 SHA-2와 디자인이 달리짐      

 

'Python 프로그래밍' 카테고리의 다른 글

[Python] Object Reference  (0) 2022.05.27
[Python] mutable과 immutable  (0) 2022.05.26
[Python(Web)] Robots.txt  (1) 2022.05.21
[Python] Decorator  (0) 2022.05.20
[Python] Example of Magic Method  (0) 2022.05.13

Robots.txt 란

 robots.txt는 웹사이트에서 크롤링하며 정보를 수집하는 검색엔진 크롤러(또는 검색 로봇)가 액세스 하거나 정보수집을 해도 되는 페이지가 무엇인지, 해서는 안 되는 페이지가 무엇인지 알려주는 역할을 하는 .txt (텍스트) 파일입니다. robots.txt 파일은 검색엔진 크롤러가 웹사이트에 접속하여 정보 수집을 하며 보내는 요청(request)으로 인해 사이트 과부하되는 것을 방지하기 위해 사용됩니다. 그리하여 robots.txt에서 액세스가 허용되지 않은 디렉토리라면 원칙적으로 크롤링하지 않습니다.

 

Robots.txt를 왜 적용해야 할까?

 검색엔진 최적화 측면에서 보았을 때 robots.txt 파일을 적용해야 하는 이유는 크게 세 가지가 있습니다. 첫 번째는 앞서 간략하게 언급한 것 같이, 검색엔진 크롤러의 과도한 크롤링 및 요청으로 인한 과부하 방지를 위해서입니다. 크롤러의 정보 수집 및 크롤링을 제한함으로써 불필요한 요청을 줄이고, 서버에서 처리해야 하는 요청을 줄여 과부하로 인한 문제가 생기는 것을 방지할 수 있습니다.

 두 번째는 검색엔진 크롤러의 일일 요청 수를 뜻하는 크롤 버짓 (또는 크롤링 예산 – Crawl budget) 낭비 방지를 위해서입니다. 검색엔진은 웹사이트에 방문해 정보를 수집할 때 웹사이트의 규모를 포함한 여러 가지 요소들을 고려하여 하루에 얼마나 많은 페이지를 방문 및 수집할지 설정합니다. 예를 들어 웹사이트에 1,000개의 페이지가 있고, 검색엔진이 일간 2,000 건의 요청 수를 보낸다고 한다면, 웹사이트에서 새로운 콘텐츠가 발행되거나, 업데이트가 있었을 경우 반나절이면 검색엔진이 변경사항에 대해 인지를 하게 된다고 볼 수 있습니다. 만약 robots.txt 파일에서 크게 중요하지 않거나 크롤링되지 말아야 하는 페이지를 지정하지 않는다면, 일일 크롤 버짓이 낭비될 수 있습니다. 결과적으로 정작 중요한 업데이트 또는 새로운 페이지가 검색엔진 결과에 반영되지 않거나 빠르게 색인되는데 불리합니다.

세 번째는 robots.txt를 이용해 검색엔진 크롤러에게 사이트맵 (sitemap.xml)의 위치를 제공하여 웹사이트의 콘텐츠가 검색엔진에게 더 잘 발견될 수 있도록 하기 위해서입니다. 물론 구글 서치 콘솔이나 네이버 서치 어드바이저와 같은 웹마스터 도구를 이용해 사이트맵을 검색엔진에 제출할 수 있지만, robots.txt에 사이트맵 디렉토리를 언급함으로써 사이트맵이 구글과 네이버를 포함한 다른 검색 검색엔진 크롤러에게 빠르게 발견될 수 있습니다.

추가적으로, SEO 측면에서의 이유는 아니지만 robots.txt를 창의적으로 이용하여 브랜딩 측면에서 좋은 이미지를 구축할 수 있습니다. 검색엔진 크롤러는 robots.txt의 표준 구문을 인식하고, 주석 처리된 문자열을 무시하기 때문에 이 특성을 이용하여 몇몇 글로벌 웹사이트들은 긍정적인 브랜드 이미지를 만드는데 robots.txt 파일을 사용하기도 합니다. 대표적으로는 에어비앤비 홈페이지를 예로 들 수 있다.

 

Robots.txt 작성

 

1. Robots.txt 작성방법

 robots.txt 작성에 앞서, 먼저 robots.txt 파일은 어떤 형식으로 작성되어야 하는지 짚고 넘어가야 합니다. ” .txt”로 끝나는 명칭에서 이미 눈치채셨을지 모르겠지만, robots.txt은 기본적으로 텍스트 파일( .txt )로 작성이 되어야 합니다. 그러기 위해서는 텍스트 파일을 편집할 수 있는 툴 (메모장, 텍스트 편집기, MS 워드) 등으로 robots.txt 파일을 작성한 뒤, .txt 파일 형식으로 저장하여야 합니다. 또한 robots.txt는 사람이 아닌 검색엔진 크롤러 (예: 구글봇, 네이버 예티, 빙봇 등)가 읽고 인식해야 하는 파일이기 때문에 정해진 형식과 문법에 따라 작성해야 합니다. 검색엔진 크롤러 별로 robots.txt 파일을 해석하는 방식이 조금씩 차이가 있다고 알려져 있으나, 기본적인 문법은 다르지 않습니다. 가장 기본적인 robots.txt 파일은 아래와 같은 형식을 가집니다.

#robots.txt 예시 - 기본 형식

User-agent: *
Disallow: /forbidden/

 robots.txt 는 기본적으로 두 가지의 정보를 포함합니다. 위의 robots.txt 예시처럼 어떤 검색엔진 크롤러를 지정할 것인지 (User-agent 값), 그리고 어떤 디렉토리를 제한할 것인지 (Disallow 값)에 대한 정보를 지정할 수 있습니다. 위의 robots.txt 기본 형식 예시는 모든 검색엔진 크롤러를 (User-agent: *) 모든 /forbidden/ 서브 폴더 이하 디렉토리의 크롤링을 제한한다 (Disallow: /forbidden/)라는 의미입니다.

 robots.txt를 구성하는 요소는 크게 네 가지가 있습니다. 각각의 구성 요소를 모두 포함할 필요는 없지만 “User-agent”는 반드시 포함되어야 합니다.

  1. User-agent: robots.txt 에서 지정하는 크롤링 규칙이 적용되어야 할 크롤러를 지정합니다.
  2. Allow: 크롤링을 허용할 경로입니다 (/ 부터의 상대 경로).
  3. Disallow: 크롤링을 제한할 경로입니다 (/ 부터의 상대 경로).
  4. Sitemap: 사이트맵이 위치한 경로의 전체 URL입니다 (https:// 부터 /sitemap.xml 까지의 전체 절대경로 URL).

 User-agent 이름은 검색엔진마다 모두 다릅니다. 가장 대표적으로 알려져 있는 구글 (Googlebot), 네이버 (Yeti), 빙(Bingbot), 야후 (Slurp) 외에도 각 검색엔진마다 User-agent 이름을 확인하여야 합니다. 아래는 자주 쓰이거나 영향력이 있는 서치 엔진별 크롤러 User-agent 이름 모음입니다. 웹사이트 크롤링 규정을 적용할 검색엔진 크롤러명을 지정하실 때 참고하시길 바랍니다.

  • 구글: Googlebot
  • 네이버: Yeti
  • 다음: Daum
  • : Bingbot
  • 덕덕고: DuckDuckBot

 위 리스트에 포함된 검색엔진 크롤러 말고도 검색엔진별 크롤러의 이름은 모두 다르며, 검색엔진 업데이트 또는 리브랜딩의 이유로 크롤러 이름이 바뀌는 경우도 드물지만 종종 있는 일입니다. 그렇기에 robots.txt를 작성하기 전에 검색을 통해 검색엔진별 크롤러 이름을 먼저 확인이 필요할 것이다.

 

 

2. Robots.txt 문법과 예시

 앞서 잠시 언급했지만, robots.txt는 사람이 아닌 검색엔진 크롤러를 위한 파일이기 때문에 특정한 형식과 문법을 지켜 작성해야 합니다. 가장 기본적인 형태의 “User-agent”와 “Disallow”를 기본 뼈대로 여러 조합을 통해 특정 크롤러 별, 특정 디렉토리 별로 크롤링 제한을 적용할 수 있습니다. 

 robots.txt 파일을 작성할 때 가장 기본적으로 명심하셔야 할 것은, robots.txt 파일에서 특정하게 명시하지 않은 크롤러와 디렉토리는 모두 크롤링이 가능한 것으로 간주됩니다. 그렇기 때문에 특별한 이유가 있는 것이 아니라면 따로 크롤링이 가능한 검색엔진 크롤러 또는 디렉토리를 지정할 필요가 없습니다. 

 아래 robots.txt 예제는 가장 기본적인 형식인 모든 또는 특정 크롤러의 특정 폴더 이하 제한 문법입니다. 아래와 같이 모든 크롤러 (*) 또는 특정 크롤러명을 입력하면 됩니다.

# 대상: 모든 크롤러
# 제한 디렉토리: /do-not-crawl-this-folder/ 이하

User-agent: * 
Disallow: /do-not-crawl-this-folder/
# 대상: 네이버 크롤러 (Yeti)
# 제한 디렉토리: /not-for-naver/ 이하

User-agent: Yeti
Disallow: /not-for-naver/

 위의 기본 형식에서는 한 가지, 또는 모든 크롤러를 한 가지 디렉토리만 제한하는 문법이었는데요, 만약 복수의 크롤러가, 특정 디렉토리에서 크롤링하는 것을 제한하고 싶다면 아래 예제와 같이 한 줄에 하나의 User-agent를 여러 개 적고, 제외할 디렉토리를 지정하면 됩니다. 여기서 주의해야 할 점은, 제한하고자 하는 크롤러를 모두 적은 뒤 디렉토리를 지정하는 “Disallow” 라인을 작성해야 한다는 점입니다.

# 대상: 네이버 크롤러 (Yeti) & 덕덕고 크롤러 (DuckDuckBot) & 다음 크롤러(Daum)
# 제한 디렉토리: /not-for-naver-and-duckduckgo-and-daum/ 이하

User-agent: Yeti
User-agent: DuckDuckBot
User-agent: Daum
Disallow: /not-for-naver-and-duckduckgo-and-daum/

 그렇다면 다수의 크롤러를 제한하되, 제한하는 크롤러마다 다른 디렉토리를 차단하고 싶은 경우는 아래 예제와 같이 User-agent를 지정해준 뒤 개별적인 디렉토리를 지정해주면 됩니다.

# 대상: 네이버 크롤러 (Yeti)
# 제한 디렉토리: /not-for-naver/ 이하
# 대상: 덕덕고 크롤러 (DuckDuckBot)
# 제한 디렉토리: /not-for-duckduckgo/ 이하

User-agent: Yeti
Disallow: /not-for-naver/

User-agent: DuckDuckBot
Disallow: /not-for-duckduckgo/

 예를 들어 네이버와 덕덕고는 같은 디렉토리 세 가지를 제한하고, 다음(Daum)은 두 가지 디렉토리를 제한하고 싶다면 아래와 같이 작성하여야 합니다. 

# 대상: 네이버 크롤러 (Yeti) & 덕덕고 크롤러 (DuckDuckBot)
# 제한 디렉토리 1: /not-for-naver-and-duckduckgo-1/ 이하
# 제한 디렉토리 2: /not-for-naver-and-duckduckgo-2/ 이하
# 제한 디렉토리 3: /not-for-naver-and-duckduckgo-3/ 이하
# 대상: 다음 크롤러 (Daum)
# 제한 디렉토리 1: /not-for-daum-1/ 이하
# 제한 디렉토리 2: /not-for-daum-2/ 이하

User-agent: Yeti
User-agent: DuckDuckBot
Disallow: /not-for-naver-and-duckduckgo-1/
Disallow: /not-for-naver-and-duckduckgo-2/
Disallow: /not-for-naver-and-duckduckgo-3/

User-agent: Daum
Disallow: /not-for-daum1/
Disallow: /not-for-daum2/

 앞서 robots.txt 에 지정되지 않은 크롤러와 디렉토리는 크롤링이 가능한 것으로 간주되기에 따로 “Allow” (크롤링 가능 디렉토리) 지정을 하지 않아도 된다고 하였습니다. Allow 디렉토리 지정의 경우 아래 예제와 같이 크롤링이 제한된 상위 서브 폴더 이하의 디렉토리 중, 특정 세부 디렉토리를 따로 크롤링 허용하고 싶을 때 사용하여야 합니다. 

# 대상: 네이버 크롤러 (Yeti)
# 제한 디렉토리: /not-for-naver/ 이하
# 허용 디렉토리: /not-for-naver/only-allow-here/ 이하

User-agent: Yeti
Disallow: /not-for-naver/
Allow: /not-for-naver/only-allow-here/

 위의 세부 디렉토리 허용 예제에서 눈치채셨을지도 모르지만, Allow (디렉토리 허용)은 Disallow (디렉토리 차단) 보다 우선권을 갖습니다. 따라서 Allow를 사용하여 세부 디렉토리 크롤링 허용을 지정하게 될 때에는 의도치 않게 Disallow와 상충되지는 않는지 잘 살펴보아야 합니다. 아래는 잘못된 Allow 사용으로 Disallow 지정이 상충된 예시입니다. 아래와 같이 제한 디렉토리보다 상위 디렉토리를 허용하게 된다면, 지정된 제한 디렉토리는 허용 디렉토리와 상충되어 무효 처리됩니다.

# 대상: 네이버 크롤러 (Yeti)
# 제한 디렉토리: /folder-depth-1/folder-depth-2/ 이하
# 허용 디렉토리: /

User-agent: Yeti
Disallow: /folder-depth-1/folder-depth-2/
Allow: /

 원하는 크롤링 제한 크롤러와 디렉토리 지정을 마쳤다면, 마지막으로는 사이트맵 위치를 명시해야 합니다. 사이트맵은 “Sitemap:”이라는 형식으로 포함할 수 있으며, 복수의 사이트맵 위치를 명시할 수 있습니다. 사이트맵 위치는 상대 주소를 사용하는 Disallow 또는 Allow 디렉토리와는 다르게, 절대 주소 (전체 URL) 포함해야 합니다. 아래는 사이트맵을 포함한 기본형 robots.txt 파일의 작성 예시입니다.

User-agent: *
Disallow: /do-not-crawl-here/
Sitemap: https://www.example.com/sitemap.xml

 상황과 목적에 맞게 robots.txt 작성을 모두 마쳤다면 텍스트 파일 (.txt) 로 저장해야 하는데요, 이때 파일명은 반드시 robots.txt 여야 합니다. 또한 업로드는 반드시 웹사이트의 루트 디렉토리 (웹사이트 주소에서 첫번째 '/' 바로 뒤) 에 업로드 되어야 한다. 네이버의 예를 들어보면 https://www.naver.com/robots.txt 에 업로드 되어있다.

데코레이터(Decorator)

 데코레이터(Decorator)란 사전적 의미로는 “장식가” 또는 “인테리어 디자이너” 등의 의미를 가지고 있습니다. 이름 그대로, 자신의 방을 예쁜 벽지나 커튼으로 장식을 하듯이, 기존의 코드에 여러가지 기능을 추가하는 파이썬 구문이라고 생각하면 된다. 

 즉, 함수를 받아 명령을 추가한 뒤 이를 다시 함수의 형태로 반환하는 함수이다. 함수의 내부를 수정하지 않고 기능에 변화를 주고 싶을 때 사용한다. 일반적으로는 함수의 전처리나 후처리에 대한 필요가 있을 때 사용한다. 또한 데코레이터를 이용해, 반복을 줄일고 메소드나 함수의 책임을 확장한다. 본인의 경우에는 파이썬을 학습하면서 새로 정의한 함수의 기능을 측정할 때 사용했다. 즉, 해당 함수가 시작될 때 시간을 측정하고 함수의 기능이 모두 종료된 후에 시간을 측정하여 그 값의 차를 구해 함수의 총 수행시간을 계산하여 출력하는 데코레이터를 만들어 활용했었다.

 

데코레이터의 구조

 함수로 만드는 데코레이터는 일반적으로 아래와 같은 구조를 가지고 있다.

def out_func(func):  # 기능을 추가할 함수를 인자로
    def inner_func(*args, **kwargs):
        return func(*args, **kwargs)
    return inner_func
def decorator(func):
    def wrapper(*args, **kwargs):
       
        print('전처리')   
        print(func(*args, **kwargs))
        print('후처리')
    return wrapper
@decorator
def example():
    return '함수'
example()
'''''''''
전처리
함수
후처리
'''''''''

 

 또한, 클래스로 만드는 데코레이터 구조는 다음과 같다.

class Decorator:

    def __init__(self, function):
        self.function = function

    def __call__(self, *args, **kwargs):
        return self.function(*args, **kwargs)
class Decorator:

    def __init__(self, function):
        self.function = function

    def __call__(self, *args, **kwargs):
        print('전처리')
        print(self.function(*args, **kwargs))
        print('후처리')
@Decorator
def example():
    return '클래스'
example()
'''''''''
전처리
클래스
후처리
'''''''''

 

 

함수형 데코레이터 사용 예제

import datetime


def datetime_decorator(func):
        def decorated():
                print datetime.datetime.now()
                func()
                print datetime.datetime.now()
        return decorated



@datetime_decorator
def main_function_1():
        print "MAIN FUNCTION 1 START"


@datetime_decorator
def main_function_2():
        print "MAIN FUNCTION 2 START"


@datetime_decorator
def main_function_3():
        print "MAIN FUNCTION 3 START"

 

  • 먼저 decorator 역할을 하는 함수를 정의하고, 이 함수에서 decorator가 적용될 함수를 인자로 받는다. python 은 함수의 인자로 다른 함수를 받을 수 있다는 특징을 이용하는 것이다. 
  • decorator 역할을 하는 함수 내부에 또 한번 함수를 선언(nested function)하여 여기에 추가적인 작업(시간 출력) 을 선언해 주는 것이다. 
  • nested 함수를 return 해주면 된다. 

 

클래스형 데코레이터 사용 예제

import datetime


class DatetimeDecorator:
        def __init__(self, f):
                self.func = f

        def __call__(self, *args, **kwargs):
                print datetime.datetime.now()
                self.func(*args, **kwargs)
                print datetime.datetime.now()


class MainClass:
        @DatetimeDecorator
        def main_function_1():
                print "MAIN FUNCTION 1 START"

        @DatetimeDecorator
        def main_function_2():
                print "MAIN FUNCTION 2 START"

        @DatetimeDecorator
        def main_function_3():
                print "MAIN FUNCTION 3 START"


my = MainClass()

my.main_function_1()
my.main_function_2()
my.main_function_3()

 decorator를 class로 사용하고 싶다면 위와 같이 __call__ 함수로 decorator 형식을 정의해 주면된다.  class의 __call__ 함수로 정의해주는게 nested 함수 형식으로 정의한 것 보다 더 깔끔해 보인다. 

 

데코레이터 실 사용 예제

학생들의 평균을 계산하는 코드를 일단 아래와 같이 작성하였다.

score = [(100, 100), (95, 90), (55, 60), (75, 80), (70, 70)]

def get_avg(score:list):
	# 리스트와 반복문을 사용
    # 학생별 평균
    for index, point in enumerate(score):
    	print(f'{index+1}번, 평균 : {sum(point)/len(point):.1f}')

만약 위의 함수가 교내 교직원만 사용되어야 하는 함수라서 교직원의 코드를 입력해야만 사용할 수 있다고 가정한다면 교내에서만 사용하는 코드를 확인하고 실행할 수 있는 데코레이터 함수를 선언할 수 있다.

def need_code(func):
	def wrapper(*args, **kwargs):
    	code = "1234"
        input_pw = input("비밀번호를 입력하세요 : ")
        if input_code == code:
        	result = func(*args, **kwargs)
        else:
        	result = "잘못된 코드입니다.
        return result
    return wrapper

 위에서 설명했듯 데코레이터를 선언하고 사용하면 결과적으로 wrapper 함수가 실행되는 것이다. input으로 코드를 입력받고 코드가 맞으면 데코레이터를 함수로 전달한 func 죽, get_avg() 함수를 실행하고, 코드가 맞지 않으면 "잘못된 코드입니다." 를 반환한다. 

 

Magic Method (Special Method)

 매직 메소드

  • Magic Method, Special Method라고도 불림
  • 파이썬이 내부적으로 구현된(빌트인) 메소드를 구현함
    • 이미 만들어짐
  • 더블 언더스코어를 사용한 것들이 Magic Method
  • 데이터 모델을 보면 자세히 나와있음
  • int, float의 메소드를 확인해보자
# 기본형
print(int)
print(float)

# 모든 속성 및 메소드 출력
print(dir(int))
print(dir(float))
  • dir 출력 결과를 보면, 더블 언더스코어로 시작하는 많은 메소드를 볼 수 있음
  • __new__
    • 새로운 인스턴스를 만들 때 제일 처음으로 실행되는 메소드
    • 새로운 object를 return
  • __init__
    • 인스턴스가 __new__로 생성된 후, 호출되는 메소드
  • __add__
    • 더하는 메소드
    • 아래 두 결과는 동일함
    • 사용하기 쉽게 +에 __add__에 매핑되었다고 생각하면 됨
      print(n+10)
      print(n.__add__(10))
      print(n.__doc__)
      print(n.__bool__(), bool(n))
    
  • __doc__
    • Docstring을 출력하는 메소드
  • __bool__
    • Boolean 유무를 나타내는 메소드
    • bool(n)과 동일한 표현
  • __mul__
    • 곱하는 메소드
    • *에 매핑됨
  • __sub__
    • 빼는 메소드
  • __le__
    • 작거나 같다
    • little or equal(<=) 표현
  • __ge__
    • 크거나 같다
    • greater or equal(>=) 표현
  • __del__
    • 객체를 없어질 때 호출되는 메소드
    • 내부에 있는 레퍼런스 카운터가 0이 되면 삭제됨
  • __bytes__
    • 객체를 나타내는 byte 문자열
  • __format__
    • 객체를 나타내는 format을 지정하고 싶을 때 사용
  • __len__
    • 객체의 길이를 반환
  • __iter__
    • 컨테이너의 iterator를 반환
  • __reversed__
    • 순서가 반대로 바뀌는 reversed() 함수 호출
  • __contains__
    • item 인자를 받고, 존재하면 True, 아니면 False
    • __contains__가 정의도지 않으면 __iter__를 통해 이터레이션을 돌며 확인함
  • 예시 Class #1
    • Class끼리 더하는건 쉽지 않음
    • 그러나 우리가 매직 메소드를 구현하면, Class끼리 더하는걸 정의할 수 있음(예를 들어 사람 Instance를 각각 더하면 두 사람의 키가 나오게하는!)
    • add할 경우 커스텀으로 함수를 수정할 수 있음(예를 들어 2개를 더할 경우 벌크 할인 느낌으로 * 0.8!
class Notebook:
    def __init__(self, name, price):
        self._name = name
        self._price = price
    
    def __str__(self):
        return f'{self.__class__.name} Class Info : {self._name}, {self._price}'
        
    def __add__(self, x):
        print("Called __add__")
        return (self._price + x._price) * 0.8
        
    def __sub__(self, x):
        print("Called __sub__")
        return self._price - x._price
        
    def __le__(self, x):
        print("Called __le__")
        if self._price <= x._price:
            return True
        else:
            return False

    def __ge__(self, x):
        print("Called __ge__")
        if self._price >= x._price:
            return True
        else:
            return False

# 인스턴스 생성  
xps13 = Notebook('XPS13', 100)
macbook_pro = Notebook('Macbook Pro', 200)

# add method를 구현하지 않았다면 아래처럼(그러지 추천하지 않음)
print(xps13._price + macbook_pro._price)

# add method를 구현했기 때문에 이렇게!
print(xps13 + macbook_pro)

print(xps13 >= macbook_pro)
print(macbook_pro - xps_13)
  • 예시 Class #2
    • *args를 사용함
    • Vector.__doc__을 할 경우 아무것도 나오지 않음(작성하지 않았으니)
class Vector(object):
    def __init__(self, *args): # unpacking, packing 가정
        '''Create a vector, example : v = Vector(5,10)'''
        if len(args) == 0:
            self._x, self._y = 0, 0
        else:
            self._x, self._y = args

    def __repr__(self):
        '''Returns the vector infomations'''
        return 'Vector(%r, %r)' % (self._x, self._y)

    def __add__(self, other):
        '''Returns the vector addition of self and other'''
        return Vector(self._x + other._x, self._y + other._y)
    
    def __mul__(self, y):
        return Vector(self._x * y, self._y * y)

    def __bool__(self):
        return bool(max(self._x, self._y))
    
# Vector 인스턴스 생성
v1 = Vector(5,7)
v2 = Vector(23, 35)
v3 = Vector()

# 매직 메소드 출력
print(Vector.__init__.__doc__)
print(Vector.__repr__.__doc__)
print(Vector.__add__.__doc__)
print(v1, v2, v3)
print(v1 + v2)
print(v1 * 3)
print(v2 * 10)
print(bool(v1), bool(v2))
print(bool(v3)) 

 


그 외 매직 메소드

  • WITH 키워드를 통해 블럭에 진입시, 컨텍스트 매니저와 함께 사용하는 매직 메소드
    • __enter__
      • with로 진입할 때 실행하는 메소드
    • __exit__
      • 블럭이 끝날 때 실행하는 메소드
  • 비동기 관련 메소드
    • __await__
      • await 표현을 사용할 수 있는 객체를 만들 때 사용
      • iterator를 반환함

'Python 프로그래밍' 카테고리의 다른 글

[Python(Web)] Robots.txt  (1) 2022.05.21
[Python] Decorator  (0) 2022.05.20
[Python] Magic Method (Special Method)  (0) 2022.05.11
[Python] Method Type  (0) 2022.05.10
[Python] Closer  (0) 2022.05.02

Magic Method (Special Method)

 파이썬에서는 객체에서 불러올 수 있는 메소드 중 __메소드이름__ 이런 식으로 더블언더스코어가 양쪽으로 붙어있는 메소드들이 있습니다.  이들은 매직 메소드 (혹은 스페셜 메소드, 혹은 던더 메소드) 라고 합니다.

 파이썬의 매직메소드는 프로그래머에 의해 직접적으로 쓰이기 보다는 객체에 어떤 액션을 취하느냐에 따라 클래스에서 내부적으로 처리됩니다.

 예를 들면, 우리는 a 와 b의 값을 더할때 a + b라고 표기합니다. 그런데 사실 a + b는 a 객체가 가지고 있는 메소드 __add__()에 의해 실행됩니다.

a+b
# 위 아래 코드는 동일
a.__add__(b)

 

각 객체가 가지고 있는 매직메소드는 dir() 를 통해 확인할 수 있습니다.

print(dir(int))

# ['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', 
# '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', 
# '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', 
# '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', 
# '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', 
# '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', 
# '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', 
# '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', 
# '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', 
# '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', 
# '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', 
# '__truediv__', '__trunc__', '__xor__', 'as_integer_ratio', 'bit_length', 
# 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']

 

Magic Method (Special Method)의 쓰임새

오퍼레이터 기능 커스터마이징

파이썬의 오퍼레이터에 대한 오버로딩을 구현할 수 있도록 돕습니다. 즉, 사용자 정의의 클래스가 빌트인 오퍼레이터 기능을 커스터마이즈해서 사용할 수 있습니다.

예시) 기본 빌트인 타입 중 int와 string은 + 연산이 불가능하죠.

x = int(1)
print(x + '1')

# 결과: TypeError: unsupported operand type(s) for +: 'int' and 'str'

임의의 클래스를 구현하여 매직메소드를 사용하면 int와 string의 + 연산결과를 얻을 수 있습니다.

#int를 부모클래스로 가진다
class Customized(int): 
# 👇str input을 받아 + 연산을 하면 str 타입으로 합체
    def __add__(self, plus: str) -> str: 
        return str(self.real) + plus

x = Customized(1)
print(x + '1')

#결과: '11'

 

인스턴스 생성

 가장 유명한 던더 메소드는 아마 __init__()일 것입니다. 그리고 __init__()과 함께 constructor 역할을 하는 __new__()라는 메소드가 있습니다. __new__()라는 메소드는 새로운 인스턴스를 생성하고 (return하고), __init__()이 return된 인스턴스를 인자에 맞게 커스터마이즈(초기화)합니다. __new__() 는 static method이며, 일반적인 경우 굳이 오버라이드 하지 않습니다.

형식과 사용법은 아래와 같습니다.

#__new__() 형식

class ClassName:
    def __new__(cls, *args, **kwargs):
        #...중간 코드...#
        return super(ClassName, cls).__new__(cls, *args, **kwargs)
# 적용 예시

class MyClass(object):
    def __new__(cls, interger:int, array:list):
        print('인스턴스 생성중...')
        return super(MyStr, cls).__new__(cls, interger, array)

    def __init__(self, interger, array):
        self.interger = interger
        self.array = array

'Python 프로그래밍' 카테고리의 다른 글

[Python] Decorator  (0) 2022.05.20
[Python] Example of Magic Method  (0) 2022.05.13
[Python] Method Type  (0) 2022.05.10
[Python] Closer  (0) 2022.05.02
[Python] 일급 객체 (First-Class Citizen)  (0) 2022.04.30

Class

 클래스는 객체(Object)를 만들기 위한 방법입니다. 클래스는 변수와 객체 그리고 관련된 기능들을 선언할 수 있습니다. 파이썬에서 클래스를 만들기 위해 'class' 키워드를 사용합니다.

 

Instance

 객체(Object)는 클래스의 생성자를 사용하여 생성합니다. 이 객체를 클래스의 인스턴스라 부릅니다.

 

Python의 매소드 종류

Python에는 3가지의 매소드 타입이 존재합니다. static 매소드, class 매소드, instance 매소드. 3가지는 각각 다른 기능을 가지며 필요에 따라 사용해야 합니다.

 

1. Static 매소드

  static 매소드는 생성할때 @staticmethod를 명시해줘야 합니다. static 매소드의 가장 중요한 특징은 인스턴스화 하지 않고 매소드를 호출 할 수 있습니다. static 메소드는 독립적으로 사용되기 때문에 다른 속성에 액세스하거나 해당 클래스 내의 다른 메소드를 호출 할 수 없습니다.

 

  class가 있을때 static 매소드를 사용할 수 있고 매소드에 엑세스 하기 위해 특정 인스턴스가 필요하지 않습니다. 예를 들어 Math라는 클래스가 있고 factorial이라는 메서드가있는 경우 static 메소드를 사용할 수 있도록 해당 메소드를 호출하기 위해 특정 인스턴스가 필요하지 않을 수 있습니다.

[ static 매소드의 예제 ]

class Math:
  @staticmethod
  def factorial(number):
        if number == 0:
            return 1
        else:
            return number * MethodTypes.factorial(number - 1)
       
factorial = MethodTypes.factorial(5)
print(factorial)

 

2. class 매소드

  class 매소드는 static 매소드와 동일한 방식으로 @classmethod를 명시해 줘야 합니다. class 매소드 또한 static 매소드와 마찬가지로 클래스를 인스턴스화 하지 않아도 호출이 가능합니다. 차이점은 다른 메소드 및 클래스 속성에 액세스 할 수있는 기능에 의존하지만 인스턴스 속성은 없습니다.

 

3. instance 매소드

  instance 매소드는 클래스를 인스턴스화 했을때만 호출이 가능합니다. 해당 클래스의 객체가 생성되면 인스턴스 메소드를 호출하고 예약어 self를 통해 해당 클래스의 모든 속성에 액세스 할 수 있습니다. instance 메소드는 새 인스턴스 속성을 creating,  getting 및 setting 하고 다른 인스턴스, 클래스 및 정적 메소드를 호출 할 수 있습니다.

class MethodTypes:

    name = "Ragnar"

    def instanceMethod(self):
        # Creates an instance atribute through keyword self
        self.lastname = "Lothbrock"
        print(self.name)
        print(self.lastname)

    @classmethod
    def classMethod(cls):
        # Access a class atribute through keyword cls
        cls.name = "Lagertha"
        print(cls.name)

    @staticmethod
    def staticMethod():
        print("This is a static method")

# Creates an instance of the class
m = MethodTypes()
# Calls instance method
m.instanceMethod()


MethodTypes.classMethod()
MethodTypes.staticMethod()

 

self와 cls의 차이점

self, cls의 키워드의 차이점은 오직 매소드 타입에 있습니다. instance 매소드를 사용하면 self 키워드를 사용하고 class 매소드를 사용하면 cls 키워드를 사용합니다. static 매소드는 속성에 접근할 수 없기 때문에 사용하는 키워드가 없습니다.

 

속성에 접근하기 위한 방법이며 매소드의 종류에 따라 self, cls 키워드를 사용한다고 생각하면 됩니다.

'Python 프로그래밍' 카테고리의 다른 글

[Python] Example of Magic Method  (0) 2022.05.13
[Python] Magic Method (Special Method)  (0) 2022.05.11
[Python] Closer  (0) 2022.05.02
[Python] 일급 객체 (First-Class Citizen)  (0) 2022.04.30
[Python] enumerate 함수  (0) 2022.04.28

 

Closer

  클로저는 어떤 함수의 내부 함수가 외부 함수의 변수(프리변수)를 참조할 때, 외부 함수가 종료된 후에도 내부 함수가 외부 함수의 변수를 참조할 수 있도록 어딘가에 저장하는 함수를 의미합니다. 클로저 함수는 아래의 조건을 충족해야합니다.  (프리변수(free variable)는 어떤 함수에서 사용되지만 그 함수 내부에서 선언되지 않은 변수를 의미합니다.)

  • 어떤 함수의 내부 함수일 것
  • 그 내부 함수가 외부 함수의 변수를 참조할 것
  • 외부 함수가 내부 함수를 리턴할 것

 

↘ Closer에 대해 자세히 알아보자

def hello(msg):
    message = "Hi, " + msg

    def say():
        print(message)

    return say

  여기 hello라는 함수가 하나 있습니다. 매개변수로 msg를 받아 message라는 변수에 문자열로 저장하고 내부함수인 say가 message를 print로 출력해줍니다. 그리고 마지막으로 say함수를(함수 실행값이 아닌 함수 자체를)리턴하고 끝납니다. 정말 단순하기 짝이없는 함수지만, 자세히 보시면 say 함수가 클로저의 조건을 모두 만족한다는 사실을 알 수 있습니다.

  1. 어떤 함수의 내부 함수일 것 - say함수는 hello함수의 내부 함수

  2. 그 내부 함수가 외부 함수의 변수를 참조할 것 - say함수는 외부 함수의 message를 참조

  3. 외부 함수가 내부 함수를 리턴할 것 - hello함수는 say함수를 리턴

즉, say함수는 클로저가 될 수 있습니다.

def hello(msg):
    message = "Hi, " + msg

    def say():
        print(message)

    return say

f = hello("Fox") # 클로저 생성
f() # 실행 결과 : "Hi, Fox" 출력

  f( ) 함수를 실행했을 때 "Hi Fox"라는 문자열이 출력되기까지의 과정을 확인해보자면

1. hello함수에 "Fox"를 매개변수값으로 넘겨주며 실행
2. message변수에 매개변수를 이용해 "Hi, Fox"라는 문자열을 저장
3. say함수가 message변수를 참조
4. say함수 리턴
5. f변수가 say함수를 참조
6. f변수 실행(say함수 실행)
7. f변수는 message변수를 출력

 위의 4번에서 hello 함수는 역할을 마치고 종료되어 메모리에서도 삭제되었을 것이다. 그러므로 hello 함수 영역의 message 변수도 삭제되었어야 한다. 하지만 어떻게 f함수가 message변수를 참조해 값을 출력한다는 것일까.... 

  이것을 가능케 한 것이 바로 클로저입니다. 중첩 함수인 say가 외부 함수인 hello의 변수 message를 참조하기에 message변수와 say의 환경을 저장하는 클로저가 동적으로 생성되었고 f가 실행될때는 해당 클로저를 참조하여 message값을 출력할 수 있는 것입니다. 이 클로저는 f변수에 say함수가 할당될 때 생성됩니다.

  이 클로저(Closure)의 영역을 찾아본다면 f.__closure__[0].cell__contents 로 찾을 수 있다. 참고로 저 __closure__ 튜플은 모든 함수 객체가 가지고 있다. 그렇기 때문에 조건을 만족하지 않아 클로저가 생성되지 않았다면, 그 값은 None으로 고정된다.

 

↘ Closer의 장점

클로저의 가장 큰 장점은 무분별한 전역변수의 남용 방지라고 생각된다. 단순히 생각하면 클로저를 쓰는 대신 전역변수를 선언해 상시 접근 가능하게 만들 수 있지만 이렇게 하면 변수가 섞일수도 있고 변수의 책임 범위를 명확하게 할 수 없는 문제가 생깁니다. 하지만 클로저를 사용하면 각 스코프가 클로저로 생성되므로 변수가 섞일 일도 없고 각 스코프에서 고유한 용도로 이용되므로 책임 범위 또한 명확해지죠.

 위의 예시에서는 내부 함수가 1개밖에 없지만 만약 내부 함수가 여러개라면? 그 여러개의 내부 함수에서 접근할 수 있게 각각 전역변수를 만들어 값을 지정한다면 코드가 지저분해지고 변수명도 점점 알아보기 힘들어지겠죠? 이를 방지해주는게 가장 큰 장점이라고 생각합니다.

 

 

 

 

 

 

'Python 프로그래밍' 카테고리의 다른 글

[Python] Magic Method (Special Method)  (0) 2022.05.11
[Python] Method Type  (0) 2022.05.10
[Python] 일급 객체 (First-Class Citizen)  (0) 2022.04.30
[Python] enumerate 함수  (0) 2022.04.28
[Python] 익명함수 lambda  (0) 2022.04.26

+ Recent posts