객체를 만들어보자!

Jo sumin·2022년 4월 2일
0

객체지향

목록 보기
1/2
post-thumbnail

📂 클래스 속 인스턴스

  • 객체에서 뭔가를 만들고 싶다고 했을 때 클래스로 이러이러한 도면으로 만들면 되겠다고 생각한다.
    클래스로 여러 객체, 인스턴스들을 만들 수 있다.
    인스타그램을 만들 때 유저를 만들려고 할 때 유저의 클래스에 팔로우, 프로필 등의 인스턴스를 만들 수 있다.
  • 클래스 이름의 첫글자는 무조건 대문자여야 한다.
class User:
	user1 = User()
    user2 = User()
    user3 = User()
    
  • 이건 int b = 5와는 다르다. User인스턴스가 만들어지고 변수 user1이 이 인스턴스를 가리킨다. 클래스 User를 이용한거다. 같은 클래스 User에 각기 다른 인스턴스 user 1,2,3이 생겼다.

예시

만약에 자동차를 만들고 싶다면, 자동차 설계도 클래스를 만들고 자동차에 필요한 기능과 부품들을 만들 수 있다.(ex: 휠이라는 부품은 나아갈 방향을 조종한다. 휠은 인스턴스다) 그리고 그 인스턴스들끼리 잘 조합해서 자동차가 나아가게 한다.(객체지향프로그래밍)



📁인스턴스 변수

class Car:

wheel.Car():
engine.Car():

wheel(인스턴스명).description(속성 이름) = "direction"
engine(인스턴스명).description(속성 이름) = "power"

형식: 🎆인스턴스명.속성명 = 데이터값🎆


속성명이 바로 인스턴스 변수다. 인스턴스 변수는 **인스턴스 개별적으로 가지고 있는 속성을 인스턴스 변수**라고 한다. 그래서 description이 인스턴스 변수인데, 여기서 wheel의 description과 engine의 description은 다른 인스턴스 변수다. (이름이 같아도)
* 인스턴스 변수를 정의하지도 않고 사용하면 Attribute Error(속성값 에러)가 당연히 나온다.

👉사용법

인스턴스.인스턴스 변수 

	wheel.description

📌 객체 = 속성(변수) + 행동(함수=메소드)


📁 메소드 3가지

메소드에는 세 가지 종류가 있다.
인스턴스 메소드, 클래스 메소드, 정적 메소드다.

📒 1. 인스턴스 메소드: 인스턴스 변수를 사용

class Car:

def Carpart(parts):
	print("당신의 부품의 기능은 {}입니다.".format(parts.description)

wheel.Car():
engine.Car():

wheel(인스턴스명).description(속성 이름) = "direction"
wheel.size = 500

engine(인스턴스명).description(속성 이름) = "power"
engine.size = 10000

Car.Carpart(wheel) #"당신의 부품의 기능은 direction 입니다."

여기서 Carparts 함수 파라미터 parts 에 wheel이나 engine이 들어갈 수 있다.
만약 Carpart(wheel)을 집어넣으면 print문에 format(wheel.description)이 들어간다.
그래서 "당신의 부품의 기능은 direction 입니다."로 출력한다.
Carpart는 description이라는 인스턴스 변수를 사용하기 때문에 인스턴스 메소드라고 할 수 있다.

👉사용법

클래스명.인스턴스 메소드 명(인스턴스명) 

	Car.Carpart(wheel)

📌인스턴스 메소드의 특별한 규칙

Car.Carpart(wheel) = wheel.Carpart()
#클래스.메소드(인스턴스) = 인스턴스.메소드()
  • 여기서 클래스.함수.인스턴스 의 결과값이랑, 인스턴스.함수()의 결과값이 같다.
    원래 wheel.part()는 함수 호출할 때 파라미터에 아무것도 집어넣지 않았으니까 None 값이라 둘이 같지 않아야 한다.
    여기서 특별한 규칙이란, 인스턴스.함수(), wheel.Carpart()는
    wheel(인스턴스)가 Carpart(함수)에 자동으로 파라미터로 넣어진다는 것이다.

  • 파라미터를 여러 개 넣어봐도,

class Car:

def Carpart(parts):
	print("당신의 부품의 기능은 {}입니다.".format(parts.description)

def MyCar(parts,your_des, your_size):
	if parts.description == "direction" and parts.size == "500":
    	print("correct")
    else:
    	print("wrong")

wheel.Car():
engine.Car():

wheel.description = "direction"
wheel.size = 500

engine.description = "power"
engine.size = 10000

Car.Carpart(wheel) #"당신의 부품의 기능은 direction 입니다."
wheel.Carpart()    #"당신의 부품의 기능은 direction 입니다."

wheel.Mycar(direction, 500)	   	# correct 로 출력됨
wheel.Mycar(wheel,direction, 500) #Type Error : 인자 하나를 받아야 하는데 두 개 받았다고 나옴. 
#함수 앞에 쓴 wheel도 파라미터로 자동할당됨.

이렇듯, 파라미터 여러개를 입력받을 때에도 맨 앞의 인스턴스가 자동 할당된다.

인스턴스 메소드의 첫번째 파라미터인 인스턴스에 초점이 맞춰진 주인공이 된다. 그 주인공이 항상 self란 걸 알면 편하기 때문에 "파이썬"에서는 self로 써주자는 규칙이 있다. (안지켜도 오류는 없지만)

인스턴스 메소드를 호출하는 자신, 인스턴스를 self라고 하니, 더 보기 편하다.

규칙⭐ 인스턴스 메소드 첫번째 파라미터는 self로 쓴다.


📌 인스턴스 변수와 같은 이름을 갖는 파라미터

class Car:

def Carpart(self):
	print("당신의 부품의 기능은 {}입니다.".format(self.description)

def check(self,description):
	return self.description == description

wheel.Car():
engine.Car():

wheel.description = "direction"
wheel.size = 500

engine.description = "power"
engine.size = 10000

Car.Carpart(wheel) #"당신의 부품의 기능은 direction 입니다."
wheel.Carpart()    #"당신의 부품의 기능은 direction 입니다."

print(wheel.check("direction")) #True
print(engine.check("direction")) #False

여기서 주목할 부분

def check(self,description):
return self.description == description

print(wheel.check("direction")) #True
print(wheel.check("power")) #False

앞의 self.description은 첫번째 파라미터.description 를 가리키고,
뒤의 description은 check(self,description)의 description이다.

  • 첫번째 print문 : wheel. description은 wheel.description = "direction"로 명시했으니 "description)이다. 그리고 == 후의 description은 파라미터 wheel의 description, "direction"이니 일치해서 boolean값 True다.
  • 두번째 print문 : self는 engine이다. wheel.description = "direction"다.
    "direction"=="power"와 다르므로 False다.

📌 인스턴스 설정을 2줄만에 : Initialize 메소드

여기서 user1의 특성이 무엇인지 굳이 정해주지 않아도, 메소드 initialize 로 self.name = name 식으로 쓰고 파라미터로 그 인스턴스의 변수의 값을 설정해주면 훨씬 간단해진다.

class User:
    
    def initialize(self, name, email, password):
        self.name = name
        self. email = email
        self. password = password
    
user1 = User()
user1.initialize("Young", "young@codeit.kr", "123456")

user2 = User()
user2.initialize("Yoonsoo", "yoonsoo@codeit.kr", "abcdef")

user3 = User()
User.initialize(user3, "Taeho", "taeho@codeit.kr", "123abc")

user4 = User()
User.initialize(user4, "Lisa", "lisa@codeit.kr", "abc123")


print(user1.name, user1.email, user1.password)
print(user2.name, user2.email, user2.password)
print(user3.name, user3.email, user3.password)
print(user4.name, user4.email, user4.password)

이제부터 새 인스턴스를 짤 때마다 두 줄씩만 입력하면 된다.

User를 생성하는 줄과 초깃값 설정하는 줄

user1 = User()
user1.initialize("Young", "young@codeit.kr", "123456")

여기서 한 줄로 더 줄일 수 있다.

인스턴스 메소드 initialize()를 __ init__()로 바꿔준다.
__init__() 메소드는 인스턴스가 생성될 때 자동으로 호출된다.
  • 이렇게 앞에 언더바 두개가 있는 메소드를 Magic Method(매직 메소드) 또는 Special Method라고 한다. 언더바(_)가 두개씩 있으니까 던더바 메소드라고도 부른다.

매직 메소드에 관한 자세한 설명은 여기 클릭👍

특정 상황에서 자동으로 호출 되는 메소드다.

class User:

    def __init__(self, name, email, password):
        self.name = name
        self. email = email
        self. password = password
    
user1 = User("Young", "young@codeit.kr", "123456")

user2 = User("Yoonsoo", "yoonsoo@codeit.kr", "abcdef")

User = User(user3, "Taeho", "taeho@codeit.kr", "123abc")

User = User(user4, "Lisa", "lisa@codeit.kr", "abc123")

print(user1.name, user1.email, user1.password)
print(user2.name, user2.email, user2.password)
print(user3.name, user3.email, user3.password)
print(user4.name, user4.email, user4.password)

결과는 똑같다.

그럼 이 한 줄에 무슨 일이 일어난걸까?

user1 = User("Young", "young@codeit.kr", "123456")
  1. User 인스턴스가 생성된다
  2. init 메소드가 자동 호출된다.
 def __init__(self, name, email, password):
        self.name = name
        self. email = email
        self. password = password

user1 = User("Young", "young@codeit.kr", "123456")

여기에서 self에는 막 생성된 User의 인스턴스명(user1)이 들어간다. 그리고 괄호 안에 있는 name, email, password 값들("Young", "young@codeit.kr", "123456")이 순서대로 init의 name, email, password에 들어간다. 
~~~python
self.name = "Young"
self.email = "young@codeit.kr"
self.password = "123456"

⭐그래서 init 메소드는 인스턴스 변수를 초기화해주는 것이 된다. init메소드는 인스턴스 생성과 인스턴스 변수 초기화를 한 줄에 해줄 수 있다.

정리해서, 다시 지금껏 줄인 단계를 한 단계씩 짚어보겠다.

Step1

class User:

user1.User():
user1(인스턴스명).name(속성 이름) = "Young"	
user1.email = "young@codeit.kr"
user1.password = "123456"

user1.메소드명()
user1.메소드명("Young", "young@codeit.kr")

Step2

class User:
    
    def initialize(self, name, email, password):
        self.name = name
        self. email = email
        self. password = password
    
user1 = User()
user1.initialize("Young", "young@codeit.kr", "123456")

Step3

class User:

    def __init__(self, name, email, password):
        self.name = name
        self. email = email
        self. password = password
    
user1 = User("Young", "young@codeit.kr", "123456")

📌 _str__ 메소드

class User:

    def __init__(self, name, email, password):
        self.name = name
        self. email = email
        self. password = password
        
    def say_hello(self):
    	print("안녕하세요! 저는 {}입니다!".format(self.name)
    
user1 = User("Young", "young@codeit.kr", "123456")

user2 = User("Yoonsoo", "yoonsoo@codeit.kr", "abcdef")

User = User(user3, "Taeho", "taeho@codeit.kr", "123abc")

User = User(user4, "Lisa", "lisa@codeit.kr", "abc123")

print(user1.name, user1.email, user1.password)
print(user2.name, user2.email, user2.password)
print(user3.name, user3.email, user3.password)
print(user4.name, user4.email, user4.password)

초기화된 인스턴스를 출력해보았다.

결과물


'어떤 클래스의 객체인지 + 인스턴스 저장 주소'가 적혀있다.

이제 원하는 상황으로 출력하고 싶어 다시 특별한 메소드를 쓰려고 한다.

⭐ _str__은 출력 print 함수를 호출할 때 자동으로 불린다.

어떤 인스턴스를 출력하면 _str__ 리컨값이 출력된다.
check only
str 메소드는 인스턴스의 정보를 이해하기 쉬운 문자열로 나타내고 싶을 때 유용하게 사용된다.


이런식으로 str메소드에 리턴값을 지정해주면 print문이 실행될 때 자동으로 str 메소드를 거쳐 출력이 되게 한다.

이렇게 인스턴스와 관련된 원하는 정보를 출력하고 싶을 때 str 메소드를 쓰면 굳이 호출해주지 않아도 (리턴값만 잘 지정해준다면) 알아서 str 메소드가 호출되어 편리해진다.


📙 2. 클래스 메소드 : 클래스 변수를 사용

📌 클래스 변수 : 한 클래스의 모든 인스턴스가 공유하는 속성

방식은 이렇게 클래스 밑에 바로 전역변수 선언하듯 써주면 된다.
값 변경할 때나 설정할 때에도, 출력할 때에도, 클래스명.클래스변수 = "값"으로 지정해주면 된다.

클래스 변수 count가 유저 인스턴스 개수를 정확히 나타내도록 하려면
init 메소드에서 count 해주면 된다.

같은 이름의 클래스 변수 VS 같은 이름의 인스턴스 변수

A. 인스턴스 변수가 읽어진다.

꼬일 수 있어서 클래스 변수는 클래스 이름으로만 설정한다.

📌 정리

클래스 변수는 클래스명. 클래스변수 혹은 인스턴스명.클래스변수로 읽을 수 있다.
하지만 클래스 변수의 값을 설정할 때는 꼭 클래스이름. 클래스 변수명으로 설정해야한다.


🟤 데코레이터(Decorator)


말그대로 add_print_to(print_hello)는 wrapper만 return한 것이다. 함수명만으로는 실행이 안된다. wrapper()라는 괄호로 파라미터가 들어갈 자리가 있어야 실행이 되는 것이다. 나는 처음에 7번째 줄 괄호 안에 무언가를 넣어야 한다고 생각했었는데 지금 코드가 wrapper는 문자열을 출력하고 original을 출력하는 것이다.

print_hello = original = print("안녕")
wrapper = print("함수시작") + original() + print("함수끝")
add_print_to(original)= add_print_to(print_hello)

문제는 간단하다. 13줄 자체에는 보기만해서는 문제가 없는데, 실행과정을 보면 return으로 wrapper만 나오니까, 11줄에서 wrapper를 수행시키게 수정하거나, add_print_to(print_hello)() 뒤에 괄호를 추가해주면 wrapper()가 된다.


실행된다.
이렇게 함수 print_hello()의 출력값을 앞뒤로 decorate하듯이 원하는 결과값으로 출력하게 되었다.
조금 떠 깔끔하게 고쳐보자.

😀 정리

다른 함수를 꾸며주는 함수를 데코레이터 함수라고 한다.

😍데코레이터 함수 쓰는 좋은 방법

꾸며주고 싶은 함수 위에 @데코레이터 함수를 쓴다.


이렇게!
다양한 함수를 데코레이터 하나를 모두 붙여주면 같은 방식으로 꾸며질 것이다.
같은 리본을 달게 된다.

📙 클래스 메소드를 @classmethod로 설정


20번째 줄의 cls.count는 앞에 클래스 메소드로 number_of_users로 지정해서
User.count와 같게 된다.
이제 메소드 number_of_users는 클래스 User도 인스턴스 user123으로도 parameter input이 가능하게 된다.

📌📌 Instance Method VS Class Method

👉 Instance Method

say_hello() : instance method

User.say_hello(user1) (selfX_파라미터로 입력해야 전달)
user1.say_hello() (self O)#인스턴스 자신이 첫번째 파라미터로 자동전달되는 것

여기서 인스턴스 자신이 첫 parameter로 자동 전달 되는 것은 self다.
자동호출 되는 것은 두 번째 줄처럼 되어있을 때만 그렇게 되었다.

👉 Class Method

number_of_users() : class method
하지만 클래스 메소드에는 두 방법 다 첫번째 파라미터로 자동 전달된다.

User.number_of_users()
user1.number_of_users()

그리고 클래스 변수로만 작성되는 함수라면 클래스 메소드로 작성이 되어야 한다.


Q1. 그럼 클래스 변수와 인스턴스 변수 둘다 쓴다면?

A. 인스턴스 메소드로 쓰기
인스턴스 메소드는 인스턴스 변수, 클래스 변수 모두 사용 가능하기 때문.
클래스 메소드는 인스턴스 변수 사용 불가, cls로 클래스 변수는 가져올 수 있지만,
클래스 메소드가 인스턴스 변수를 가져올 방법이 없다.

Q2. 인스턴스 없이도 필요한 정보가 있다면

A. 클래스 메소드로 쓴다.
예를 들어 앞의 User.count 메소드는 인스턴스가 없더라도 필요하다.
이렇게 유저 인스턴스가 하나도 없더라도 사용할 가능성이 있으면 클래스 메소드를 만들어야 한다.
보통 클래스 메소드는 아래 예시처럼 모든 인스턴스에게 공통된 값을 적용할 때 사용된다.
클래스명.클래스메소드로 주로 쓴다.


🔥클래스 메소드 문제


👉 name, email, password 클래스 변수를 만들어 인스턴스에게 같은 성질을 부여했다.


@classmethod
def from_string(cls, string_params):
    # 유저 인스턴스를 생성해서 리턴한다
    return User("강영훈", "younghoon@codeit.kr", "123456")
@classmethod
def from_string(cls, string_params):
    # 유저 인스턴스를 생성해서 리턴한다
    return cls("강영훈", "younghoon@codeit.kr", "123456")

둘은 같은 의미의 코드다.


인스턴스를 통해 클래스 변수의 값을 바꾸려고 하면 클래스 변수의 값이 바뀌는 게 아니라 같은 이름의 새로운 인스턴스 변수가 만들어진다. 클래스 변수의 값을 바꾸고 싶으면 인스턴스가 아닌 클래스로 접근해서 값을 바꿔야 한다.

📘 3. 정적 메소드(Static Method)

  • 인스턴스 변수, 클래스 변수 중 아무것도 사용하지 않을 메소드일 때 쓴다.
    어떤 속성을 다루지 않고, 기능만을 정의할 때 정적 메소드를 쓴다.
class User:
    count = 0
    
    def __init__(self, name, email, pw):
        self.name = name
        self.email = email
        self.pw = pw
    
        User.count += 1
    
    def say_hello(self):
        print("안녕하세요! 저는 {}입니다!".format(self.name))
    
    def __str__(self):
        return "사용자: {}, 이메일: {}, 비밀번호: ******".format(self.name, self.email)
    
    @classmethod
    def number_of_users(cls):
        print("총 유저 수는: {}입니다".format(cls.count))
    
    @staticmethod
    def is_valid_email(email_address):
        return "@" in email_address
  • 여기서의 is_valid_email이 정적 메소드다.
  • 기능만을 정의한다.
  • 여기서도 데코레이터를 써 @staticmethod로 정적 메소드를 만들어 준다.
  • 자동 전달 되는 파라미터 없다. (self나 cls처럼)

    print(User.is_valid_email("taehosung"))
    print(User.is_valid_email("taehosung@codeit.kr"))
    print(user1.is_valid_email("taehosung"))
    print(user1.is_valid_email("taehosung@codeit.kr"))

    인스턴스, 클래스 두가지를 통해 모두 가능하다.

    ⭐ 추가로 배운 것

  1. LEGB
    LEGB 규칙
  2. 바인딩 개념
    바인딩 개념
  3. vars()함수로 추가된 인스턴스 변수도 모두 출력가능
    vars()
profile
TIL_기술블로그

0개의 댓글