Python Advanced

Python 함수

  • 특정한 기능을 반복적으로 실행할 수 있도록 도와주기 때문에 반복적인 작업들을 하지 않아도 된다.
  • 어플리케이션 모듈화에도 영향을 미치며 재사용할 수 있다.
  • ‘built-in functions’ 라고 불리는 파이썬 설치와 함께 사용될 수 있는 내장된 함수들이 있다.
    이와 별개로 새로 만들어지는 함수들은 ‘user-defined functions’라고 불립니다.

파라미터

참조 vs 값?

  • 파이썬에서 인수가 참조로 함수에 전달된다.
  • 객체의 주소값을 전달한다는 뜻
  • 참조로 전달되지만 immutable, 즉 변경이 불가한 객체는 값으로 전달된다.
  • 따라서 int, str 등은 값으로 전달되며, list, dict 와 같은 객체들은 참조값으로 전달이 됩니다.

인수

  • 필수 인수 (required arguments): 위치를 지키며 전달되는 인수
  • 키워드 인수 (keyword arguments): 파라미터 이름을 사용해 전달되는 인수
  • 기본 인수 (default arguments): 파라미터에서 기본으로 사용되는 값

Python 클래스

  • 파이썬의 작동 방식은 고급 스크립트 방식이고, 언어의 설계는 객체지향언어를 기반으로 만들어져 있다.
  • 파이썬은 모든 것이 객체
  • 객체를 다루기 위해서 클래스에 대해 알고 있어야 한다.
  • 객체지향언어는 대규모 프로그램의 설계를 위해 만들어진 방법론
  • 설계 중심의 사고와 재사용성을 줄이기 위해 만들어져 있다.

클래스 생성

1
2
3
4
5
class Pokemon: # 생성
    pokemon_a = 'pikachu'

# 클래스 속성 접근
Pokemon.pokemon_a # 'pikachu'
  • 함수를 클래스 내에서 선언하는 것도 가능하다. 이렇게 클래스 내에서 작동하게 되는 함수를 메소드 (method)라고 부른다.
1
2
3
4
5
6
7
class Pokemon:
    pokemon_a = "pikachu"
    
    def print_pika():
        print("Hello Pika")

Pokemon.print_pika() # 'Hello Pika'
  • 인스턴스에서 해당 메소드를 실행하게 되면 자신을 인수로 넘겨주게 되기 때문에 클래스를 인스턴스를 만들게 될 때 위 함수를 인스턴스에서 활용하기 힘들다.
  • 따라서 아래와 같이 함수를 작성해야 인스턴스에서 활용이 가능하다.
1
2
3
4
5
class Pokemon:
	pokemon_a = 'pikachu'

	def print_pika(self):
		print("pika")

self

  • 인스턴스에서 메소드나 특성을 사용할 때 첫번째 인수, 파라미터로 넘겨지고 받아져야한다.
1
2
3
poke_a = Pokemon()

poke_a.print_pika() # 'pika'

생성자 함수

  • 클래스가 인스턴스화(instantiate) 될 때 사용되는 함수
    • 새로운 인스턴스를 생성하게 될 때 생성자 함수가 따로 정의되어 있지 않은 경우에 파이썬의 기본 생성자 함수를 사용하게 되어 있다.
1
2
3
4
5
6
class Pokemon:
    def __init__(self, pokemon_a='pikachu'):
        self.pokemon_a = pokemon_a

poke_a = Pokemon()
print(poke_a.pokemon_a) # 'pikachu'
  • 위 코드처럼 클래스를 기반으로 생성되는 인스턴스는 클래스의 생성자 함수에 따라 인스턴스 초기 속성을 받을 수 있다.
  • 하지만 이는 인스턴스마다의 속성이지 클래스 전체에 대한 속성은 아니다.
  • 따라서 클래스 자체에서는 이러한 속성에 대한 접근이 힘들다.

클래스 특별 메소드

  • 파이썬 클래스에서 사용되는 특별 메소드

@property

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Person:
	def __init__(self, first_name, last_name):
		self.first_name = first_name
		self.last_name = last_name
		self.full_name = self.first_name + ' ' + self.last_name

sixmini = Person('윤민', '이')

print(sixmini.first_name) # '윤민'
print(sixmini.last_name) # '이'
print(sixmini.full_name) # '윤민 이'

sixmini.first_name = '윤혁'

print(sixmini.first_name) # '윤혁'
print(sixmini.full_name) # '윤민 이'
  • 원했던 것은 first_namelast_name을 합치는 것인데 딱 한번, 생성자 함수가 실행될 때에 값이 정해지고 바뀌지 않는다.
  • 해결하려면 아래와 같이 바꿔야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person:
	def __init__(self, first_name, last_name):
		self.first_name = first_name
		self.last_name = last_name

	def full_name(self):
		return self.first_name + ' ' + self.last_name

sixmini = Person('윤민', '이')

sixmini.first_name = '윤혁'

print(sixmini.first_name) # '윤혁'
print(sixmini.full_name()) # '윤혁 이'
  • 이렇게 클래스의 특성이 아닌 하나의 메소드로 접해야 하는데, 이 때 등장하는 것이 @property이다.
1
2
3
4
5
6
7
8
class Person:
	def __init__(self, first_name, last_name):
		self.first_name = first_name
		self.last_name = last_name

	@property
	def full_name(self):
		return self.first_name + ' ' + self.last_name
  • 해당 메소드를 클래스의 특성(attribute)처럼 접근할 수 있다.
1
2
3
4
5
6
sixmini = Person('윤민', '이')

sixmini.first_name = '윤혁'

print(sixmini.first_name) #=> '윤혁'
print(sixmini.full_name) #=> '윤혁 이'
  • 이런 식으로 클래스 내에 다른 특성들과 연관이 되어 있는 특성들을 관리할 때 사용할 수 있다.

getter, setter

  • full_name을 설정하면 first_name을 바꿔주는 방법
  • 해당 특성을 가져오거나 값을 설정할 때 사용하는 메소드를 통해 섬세한 관리가 가능하다.
  • getter : 값을 가져올 때 사용하는데 property를 사용하면 자동으로 설정
  • setter : 값을 주는 행동
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Person:
	def __init__(self, first_name, last_name):
		self.first_name = first_name
		self.last_name = last_name

	@property
	def full_name(self):
		return self.first_name + ' ' + self.last_name
		
	@full_name.setter
	def full_name(self, new_full_name):
		first_name, last_name = new_full_name.split()
		self.first_name = first_name
		self.last_name = last_name

sixmini = Person('윤민', '이')

print(sixmini.first_name) #=> '윤민'
print(sixmini.full_name) #=> '윤민 이'

sixmini.full_name = '채영 한'

print(sixmini.first_name) #=> '채영'
print(sixmini.last_name) #=> '한'
print(sixmini.full_name) #=> '채영 한'

_, __

  • 변수나 함수에 특별한 의미를 부여할 때 사용
  • 생성자 함수는 클래스를 통해 인스턴스를 생성하게 해주는 특별한 용도의 함수이기 때문에 앞 뒤로 두개의 밑줄을 붙여준다.

_

  • 따로 변수나 값을 저장할 때 사용
1
2
3
4
5
6
7
8
class Pokemon:
    _pokemon_health = 100

    def __init__(self, pokemon_a='pikachu'):
        self.pokemon_a = pokemon_a

poke_a = Pokemon()
print(poke_a._pokemon_health) # 100
  • 클래스 내부 변수나 값으로 사용하더라도 파이썬에서는 외부에서 접근할 수가 있다.

__

1
2
3
4
5
6
7
8
class Pokemon:
	  __pokemon_health = 100

    def __init__(self, pokemon_a='pikachu'):
        self.pokemon_a = pokemon_a

poke_a = Pokemon()
print(poke_a.__pokemon_health) #=> 에러
  • 파이썬의 Name Mangling으로 인해 에러가 발생한다.
  • 밑줄을 두 개 사용하면 정해준 이름을 사용하지 않고 _<클래스 이름>_<변수 혹은 함수 이름>으로 변경된다.
  • __pokemon_health -> _Pokemon__pokemon_health
  • 파이썬에서는 온전한 private 개념이 없다.

Python 데코레이터

  • 어색하고 반복적인 함수의 표현을 줄이기 위해 제안
  • 함수뿐 아니라 클래스, 함수, 제너레이터 등의 다른 타입에서도 사용

함수 데코레이터

1
2
3
4
5
6
7
8
9
10
11
def my():
    print("first")
    print("my 라는 함수")

def mine():
    print("first")
    print("mine 이라는 함수")

def iam():
    print("first")
    print("iam 이라는 함수")
  • 만약 10번씩 반복해야 할 때
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def first_deco(func): # func 는 실행할 함수
    def first(): # 실행할 함수를 감싸는(wrap) 함수
        print("first")
        func()
        print("last")
    return first

@first_deco
def my():
    print("my 라는 함수")

@first_deco
def mine():
    print("mine 이라는 함수")

@first_deco
def iam():
    print("iam 이라는 함수")
  • 인자값이 저장되어 있을 땐 데코레이터에도 인자 값을 처리하는 *args, **kwargs 구문이 필요
1
2
3
4
5
6
7
8
9
10
def first_last_deco(func): # func 는 실행할 함수
    def first_last(*args, **kwargs): # 실행할 함수를 감싸는(wrap) 함수
        print("first")
        func(*args, **kwargs)
        print("last")
    return first_last

@first_last_deco
def you(name):
    print(f"{name}! Hello")

Why use

  • DRY(Don’t Repeat Yourself) : 코드 반복 사용을 줄이기 위한 원칙
  • DRY를 위해 개발자는 상속, 디스크립터, 제너레이터 등 다양한 기술을 사용하며, 데코레이터 또한 코드의 재사용을 줄이기 위한 기술 중 하나
  • 딥러닝 텐서플로우의 @tensorflow.function
1
2
3
4
5
@tensorflow.function
def calc(a, b):
    answer1 = a * b 
    answer2 = a * b
    return answer1, answer2

Reference

0%