python의 class (매서드, 인스턴스 추가 공부 필요)

yuns_u·2021년 9월 28일
0

소개

파이썬의 작동 방식은 고급 스크립트 방식이고 파이썬 언어의 설계는 객체지향언어를 기반으로 만들어져있다.
파이썬에서는 모든 것이 객체이다.
이러한 객체를 다루기 위해서는 class에 대해 잘 알고 있어야 한다.

객체지향언어는 대규모 프로그램의 설계를 위해 만들어진 방법론이다.
객체지향언어의 기술들은 설계자에게 유용하다.
어떤 집을 만드는 설계도가 있으면 같은 집을 만들 수도 있고 설계도를 활용해서 더 좋은 집을 만들 수도 있다.
클래스가 그 설계도와 같은 역할을 한다. 반복해야하는 일을 줄이고 한 곳에 설명하는 것이다.
프로그래밍언어론의 입장에서 높은 수준의 프로그래밍 언어의 기술은 했던 일을 반복하지 않기 위해서 개발된 것이기 때문이다. 즉, 설계 중심의 사고와 재사용성을 줄이기 위해서 클래스를 만들었다고 할 수 있다.

파이썬의 클래스에서는 함수와 속성 두 가지를 모두 담을 수 있다.
따라서 다양한 정보와 기능들을 묶어서 사용할 수 있다.

클래스 생성

기본적으로 클래스를 다음의 예시처럼 만들어 보았다.

class Pokemon:
   pokemon_elect = 'pikachu'

위의 코드처럼 클래스를 생성하게 된다면 다음처럼 클래스의 속성에 접근할 수 있다.

Pokemon.pokemon_elect
#=> 'pikachu'

이뿐만 아니라 함수를 클래스 내에서 선언할 수 있다.
이렇게 클래스 내에서 작동되는 함수를 method(매서드)라고 한다.

class Pokemon:
    pokemon_elect = 'pikachu'
    
    def print_pika():
        print("Pika Pika!")

위 코드처럼 일반적인 함수와 동일하게 클래스 내에서 함수를 선언해도 작동에 문제가 없다.

Pokemon.print_pika()
#=> "Pika Pika!"

그러나 클래스의 인스턴스를 만들게 되면 위 함수를 인스턴스에서 활용하기는 어렵다.
왜냐하면 인스턴스에서 해당 매서드를 실행하게 되면 자신(self)을 인수로 넘겨주게 되기 때문이다.

method(매서드)

instance (인스턴스)

self

파이썬에서 활용되는 이 self라는 단어 자체는 키워드가 아니다.
즉, 다른 단어로 대체되어도 작동에 문제가 없다.
인스턴스에서 메서드나 특성을 사용할 때 첫 번째 인수, 파라미터로 넘겨지고 받아지는 기능을 하는 것으로 self를 대중적으로 사용한다.

위의 Pokemon 클래스를 활용해 인스턴스를 생성하고 print_pika 매서드를 불러와본다.

poke_a = Pokemon()
poke_a.print_pika()
#=> "Pika Pika!"

이 때 함수를 호출할 때 인수로 아무것도 넘기지 않고 함수만 실행했다.
다만 메서드 내에서 self라는 파라미터를 하나 받도록 설정했다.
이렇게 설정할 수 있는 이유는 파이썬에서 인스턴스가 매서드나 특성을 사용할 때 자동으로 그 인스턴스에 대한 참조값을 넘기지만 자동으로 받도록 하지 않기 때문이다.

따라서 함수를 실행하게 될 때는 poke_a인스턴스에 대한 참조값이 자동으로 넘겨지지만 실제로 함수를 실행할 때 파라미터 설정이 되어있지 않은 경우에는 필수 인수가 0이기 때문에 불일치한 현상이 발생하는 것이다.

생성자 함수

클래스에 있는 함수 중에서 특별한 함수들이 몇 가지 있다.
그 중에서 생성자 함수는 클래스가 인스턴스화(instantiate)될 때 사용되는 함수이다.
새로운 인스턴스를 생성하게 될 때, 생성자 함수가 따로 정의되어 있지 않은 경우에는 파이썬의 기본 생성자 함수를 사용하게 되어 있다.

이 생성자 함수는 다음과 같이 사용할 수 있다.

class Pokemon():
    def __init__(self, pokemon_elect='pikachu')
        self.pokemon_elect = pokemon_elect
        
poke_a = Pokemon()
print(poke_a.pokemon_elect)
#=> 'pikachu'

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

class Pokemon은 인스턴스의 속성에 접근할 수 없다는 것을 실제로 실행해보면 아래와 같은 에러를 통해 확인할 수 있다.

Pokemon.pokemon_elect
#=> AttributeError: type object 'Pokemon' has no attribute 'pokemon_elect'

클래스 특별 method

파이썬에는 클래스에서 사용되는 특별 메소드들이 존재한다.
예를 들어 클래스를 생성할 때 쓰이는 생성자 함수, 즉 __init__처럼 파이썬에서 사전에 정의가 되어 있는 메소드들이 존재한다.

@property

파이썬에서는 클래스를 만들고 해당 클래스의 특성들을 설정해줄 수 있다.

이렇게 클래스 특성들을 생성할 때에는 self를 통해서 생성해줄 수 있지만 여러 변수가 연결되거나 특별히 관리가 필요한 경우 등에서는 따로 함수로 작동하는 방식으로 관리해줄 수 있다.

코드로 예시를 들기 위해 사람의 이름과 성을 받아 풀네임이라는 특성을 가지는 클래스를 만들어보면 아래와 같이 나타낼 수 있다.

class name:
    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

위의 클래스를 사용하면 아래처럼 나타낼 수 있다.

aboutMe = name('Yunsu', 'Lee')

print(aboutMe.first_name) #=> 'Yunsu'
print(aboutMe.last_name) #=> 'Lee'
print(aboutMe.full_name) #=> 'Yunsu Lee'

원했던 것은 first_namelast_name을 합치는 것인데 생성자 함수가 실행될 때 한 번 값이 정해지고 바꾸기가 쉽지 않다. 이것은 full_name을 변경해도 다르지 않다.

aboutMe = name('Yunsu', 'Lee')
aboutMe.full_name = 'Kathy Lee'

print(aboutMe.first_name) #=> 'Yunsu'
print(aboutMe.full_name) #=> 'Kathy Lee'

이 문제를 해결하기 위해 메서드를 사용해볼 수 있다.

class name:
    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

위와 같이 코드를 변경하게 되면 그래도 first_name이나 last_name이 변경하게 될 때 full_name 메소드를 사용해서 동적으로 변경되는 것을 아래처럼 확인할 수 있다.

aboutMe = name('Yunsu','Lee')

aboutMe.first_name = 'Kathy'

print(aboutMe.first_name) #=> 'Kathy'
print(aboutMe.full_name) #=> 'Kathy Lee'

위 코드처럼 first_name과 공이랗게 접근할 수 있는 것이다.
이런 방식으로 클래스 내에서 다른 특성들과 연관되어 있는 특성들을 관리할 때 사용할 수 있다.

getter 그리고 setter

이번에는 full_name 이 설정된 뒤 first_name 을 바꿔주는 방법을 살펴보고자 한다.

@property 사용하게 되면 클래스의 특성으로 만들어주는 것도 좋지만 더 나아가서 해당 특성을 가져오거나 값을 설정할 때 사용하는 메서드를 통해서 더 섬세한 관리를 할 수 있다.

getter는 어떤 것을 가져올 때 사용된다.
@property 를 통해서 메소드를 특성처럼 취급해서 가져올 수 있다. 이렇게 특성으로 만들어주게 되면 그 뒤에는 해당 특성 이름을 사용해서 setter를 설정할 수 있다. 즉, @full_name.setterfull_name을 변경할 때 어떻게 바꾸는지 알려주는 함수이다.

gettersetter를 사용하여 나타내보면 아래와 같이 나타낼 수 있다.

class name:
    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
        

위 코드의 작동 방식을 하나씩 살펴보면 아래와 같다.

aboutMe = name('Yunsu', 'Lee')

print(aboutMe.first_name) #=> 'Yunsu'
print(aboutMe.last_name) #=> 'Lee'

aboutMe.full_name = 'Hermione Granger'

print(aboutMe.first_name) #=> 'Hermione'
print(aboutMe.last_name) #=> 'Granger'
print(aboutMe.full_name) #=> 'Hermione Granger'

❓ 만약 setter 메소드인 @full_name.setter가 없다면 위 코드는 어떤 실행 결과를 보일까?

___

생성자 함수명은 __init__이다. 이 생성자 함수의 글자 양 옆의 두 개의 밑줄 표시에 대한 내용이다.

이러한 밑줄 표시는 변수나 함수에 특별한 의미를 부여할 때 사용된다.
생성자 함수는 클래스를 통해서 인스턴스를 생성하게 해주는 특별한 용도의 함수이기 때문에 앞뒤로 두 개의 밑줄을 붙여주어 특별한 의미를 부여한다.
이 두 개의 밑줄을 'double underscore' 혹은 'dunderscore'라고도 부른다.

_ (single underscore)

하나만 사용하는 경우이다.
파이썬 클래스 내부에서 따로 변수나 값을 저장할 때 사용되기도 하며 외부에서 접근할 수 있다.

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

❗️ 다만 이렇게 클래스 내부 변수나 값으로 사용하더라도 파이썬에서는 외부에서 접근할 수 있다. 클래스 외부에서 _pokemon_health를 프린트하면 그 값이 출력되는 것을 통해 알 수 있다.

__ (double underscore)

파이썬 클래스 내부에서만 관리하도록 밑줄을 두 개를 사용할 수 있다.

single underscore에서 사용한 예시를 활용하면 double underscore는 아래처럼 나타낼 수 있다.

class Pokemon:
    __pokemon_health = 100
    
    def __init__(self, pokemon_a = 'pikachu'):
        self.pokemon_a = pokemon_a
        
poke_a = Pokemon()

print(poke_a.__pokemon_health) #=>  ERROR

예상과 달리 밑줄 두 개를 사용하는 경우에는 에러가 발생한다.
이는 파이썬의 Name Mangling으로 인해 발생하는 것이다.
밑줄을 두 개 사용하게 된다면 정해준 이름을 사용하지 않고 {클래스 이름}_{변수 혹은 함수 이름}으로 변경된다.

print(poke_a._Pokemon__pokemon_health) #=> 100

즉, 위에서는 __pokemon_healthpoke_a._Pokemon__pokemon_health로서 불러낼 수 있게 된다.
(poke_a = Pokemon()이므로 poke_a 라는 메모리 속에서(.) 클래스Pokemon을 불러오게 되므로 앞에 _를 붙이고 __pokemon_health를 불러오게 된다.)

이러한 특성을 통해 파이썬에서는 무언가 숨기는 private한 개념이 특별히 존재하지 않다. (관련 용어로 pythonic이 있다.)
이러한 방법들을 통해서 파이썬 클래스 내부 값들을 외부에서 접근하기 힘들게 할 수 있지만 접근이 불가하지 않다는 것을 알 수 있다.

컨벤션

___에서 보여진 것처럼 파이썬에서는 보통 _를 사용해서 클래스 내부 변수 혹은 함수이다를 암시한다.
이를 보고 해당 클래스를 사용하느 사람들은 밑줄이 존재하는 값을 유의하며 다룰 수 있게 되는 효과가 있다.
그러나 이러한 관습은 암묵적인 부분인만큼 강제되는 것은 아니다.

profile
💛 공부 블로그 💛

0개의 댓글