Python
20210123_Python 와 Ruby 비교03, 객체지향, class, instance
생활코딩 "python&ruby"강의 참고
Python vs Ruby (루비 위주)
객체 지향 프로그래밍 (Object Oriented Programming)
- 철학적이지만, python과 ruby의 기능을 배우는 것으로 생각하는게 구체적인 느낌으로 배울수 있을 것이다.
- 모듈(module)은 함수를 수납하여 복잡도를 단순화 시킨다.
- 클래스(class)는 모듈처럼 그룹핑을 통한 수납이 가능한데, 모듈과 다르게 연관된 함수와 변수를 수납한다.
- 과정 class를 복제하여 instance를 만든다.
- 그런데 이 instance는 class와 똑같은 변수와 함수를 품지만, 변수의 값만 각각 바뀌어 품고 있는 공통된 함수를 이용해 결과를 낸다.
1. 객체 지향 프로그래밍의 사례
1) 문자열(string) 클래스 사용 사례
name1 = 'tom' # 문자열 만드는 방법 1 (생성시 코드 효율성을 위해서)
name2 = String.new('jerry') # 문자열 만드는 방법 2 (원래)
name3 = String.new('snoopy')
puts name1.reverse()
puts name1.upcase()
puts name1.size()
String
: 클래스 ,.new(값)
: 생성 함수를 통해 -> 인스턴스화가 됨- 이를 사용하기 위해서는
name1
이라는 변수에 할당시켜야 한다. - 이렇게 인스턴스가 되면 기본적으로 가지고 있는 내장함수를 사용하여
name1
을 변화 시킬수 있게 된다.
2) 배열(array) 클래스 사용 사례
names = Array.new()
names.push('tom')
names.push('jerry')
names.push('snoopy')
puts names
print names
puts names.join('-') # join 메서드는 값들을 -로 연결해줌
Array
: 클래스 ,.new()
를 통해 생성하고names
에 할당names
인스턴스의push
함수를 통해 값을 추가
2. 클래스 만들어 보기
1) Ruby
- Ruby에서 생성자
class Cal
def initialize(v1, v2) # initialize 생성자
p v1, v2
end
end
c1 = Cal.new(10, 10)
# 10
# 10
Cal
이라는 클래스를 만드는데 항상 첫글자는 대문자initaialize
라는 생성자 함수를 지정함으로서 인스턴스 생성시 제일먼저 생성자 함수가 실행되게 약속 되어 있고 이를 통해 초기화를 시키는 것임- 어차피 생성자 함수는 인스턴스가 만들어 지자 마자 실행되는 것이기에
p v1, v2
은 입력값을 확인용으로만 넣었다.
- Ruby에서 클래스 함수(메소드)
class Cal
def initialize(v1, v2) # initialize 생성자
# p v1, v2
@v1 = v1
@v2 = v2
end
def add()
return @v1+@v2
end
def subtract()
return @v1-@v2
end
end
c1 = Cal.new(10, 10)
p c1.add() # 10 ,10 더한 -> 20 리턴
p c1.subtract() # 10 ,10 을 뺀 -> 0 리턴
c2 = Cal.new(30,20)
p c2.add() # 30 ,20 더한 -> 50 리턴
p c2.subtract() # 30 ,20 을 뺀 -> 10 리턴
- 생성자에서 선언한 매개변수의 경우 그냥은 다른 함수에서는 사용을 못한다.(공유X) 이를
지역 변수
라고 함 - 공유하기 위해선
@v1
처럼@
를 붙여 공유할수있는 변수를 만들어 주어야 한다. 이 변수를인스턴스 변수
라고 한다. - 이를 통해 다른 함수에서
@
를 붙여 사용하면 공유가 된다.
2) Python
- Python에서 생성자와 메소드
class Cal(object):
def __init__(self, v1, v2) # __init__ 생성자
print(v1, v2)
c1 = Cal(10, 10)
# 10 10
- 생성자
Cal
이라는 클래스를 만드는데 항상 첫글자는 대문자로 하는게 좋다.__init__
라는 생성자 함수를 지정함으로서 인스턴스 생성시 제일먼저 생성자 함수가 실행되게 약속 되어 있고 이를 통해 초기화를 시키는 것임- 어차피 생성자 함수는 인스턴스가 만들어 지자 마자 실행되는 것이기에
print(v1, v2)
은 입력값을 확인용으로만 넣었다. - 루비와 제일 다르 점은 생성자 명칭도 다르지만
self
이다. 관습상self
로 칭하고 python에서 생성자 선언시 제일 첫번째 매개 변수가 항상 자기자신 인스턴스를 가르킨다.
class Cal(object):
def __init__(self, v1, v2):
print(v1, v2)
self.v1 = v1
# 첫번째 매개변수를 꼭 정의해야함(뭐가 되든 상관 없고) python에서 는 @가 self이고 인스턴스가 self로 들어옴
self.v2 = v2
def add(self): # 메소드 첫번째 인자가 인스턴스를 가르킴
return self.v1+self.v2
def subtract(self):
return self.v1-self.v2
c1 = Cal(10, 10)
# Cal이라는 클래스를 복제한 인스턴스를 return해주는 역할 -> c1에 담아서 c1을 통해 인스턴스를 가르킬수 있음
print(c1.add())
print(c1.subtract())
c2 = Cal(30, 20)
print(c2.add())
print(c2.subtract())
- 메소드
- Ruby처럼 다른 함수인 메소드에서 생성자에서 선언한 변수를 사용하기 위해서는
self
를 사용한다. Ruby에서@v1 = v1
처럼-> Python에서는self.v1 = v1
의 형식으로 사용하며 또 다른 함수 선언에서 첫 매개변수에self
를 적어 주어야 공유가 가능하다.
3. 객체지향의 중요성
- ruby 객체지향이 아닌 버전으로 만들어 보기
def add(v1, v2)
return v1+v2
end
def subtract(v1, v2)
return v1-v2
end
p add(10, 10) # 1. 다른 함수에서 같이 사용하는 값에 대한 관련성은 모른다.
p subtract(10, 10)
p add(30, 20)
p subtract(30, 20)
num1 = 10
num2 = 10
num3 = 30
num4 = 20
p add(num1, num2) # 2. 이렇게 하면 관련성을 알수 있고 유지보수도 할수 있지만 코드가 길어지고 커짐
p subtract(num1, num2)
p add(num3, num4)
p subtract(num3, num4)
객체 지향 사용시 객체의 확인 작업과 지정 작업이 들어가서 오히려 코드가 길어지는 것 같으나 프로그램이 커질경우 복잡도를 낮추기 위해서 객체 지향이 필요하다.
그리고 객체 지향시 소속확인을 통해 관련성을 알수 있다. 이를 통해서 값에 대한 처리에 대한 의미가 분명하게 드러나고 복잡한 상태의 프로그램일 경우, 안전하게 값을 저장하고 있기 때문에 영향을 덜 받는다.
로직보다 data가 중요하다. 물론 하드웨어 적으로 cpu가 ssd 보다 비싸지만 어떤데이터가 들어 있는냐에 따라서 그 가치 비교는 완전 달라진다.
이렇게 데이터는 가치있기에 변수보존이 중요하기 때문에 객체 지향은 중요하다.
4. 인스턴스 변수의 특성
- 인스턴스 변수는 클래스내 다른 변수들 간에 연계하여 쓸수 있는 변수이다.
- 하지만 인스턴스 변수를 쓰고 출력하는 방법이 ruby와 python에서 달라진다.
1) Ruby 인스턴스 변수 특성
class C
def initialize(v)
@value = v
end
def show()
p @value
end
end
c1 = C.new(10)
# p c1.value() # error
# c1.value = 20 # error
c1.show()
- Ruby에서는 인스턴스 변수를 밖에서 직접적으로 접근할수가 없다.
p c1.value()
-> value라는 메소드를 찾기 때문에 difine error가 난다.cl.value = 20
-> 이렇게 값을 쓰는 것도 불가능하다.- 그래서
show()
메서드를 따로 지정해서 메서드 호출을 통한 인스턴스 변수 값에 접근 가능하다.
2) Python 인스턴스 변수 특성
class C:
def __init__(self, v):
self.value = v
def show(self):
print(self.value)
c1 = C(10)
print(c1.value)
c1.value = 20 # 읽어 왔으면 쓰기도 가능함
print(c1.value)
c1.show() # 메서드 안에서 지정하여 밖에서는 메서드를 통해서 접근하는것도 가능
- Ruby와 다르게 Python에서는 직접적으로 접근이 가능하다.
print(c1.value)
,c1.value = 20
을 통해 인스턴스 변수를 읽고 쓰기가 가능하다.- 또한 따로 메서드
show()
를 지정하여 밖에서 메서드 호출로도 가능하다.
3) Ruby 인스턴스 변수 읽고 쓰기를 위한 get ,set 메서드
class C
def initialize(v)
@value = v
end
def show()
p @value
end
def getValue()
return @value
end
def setValue(v)
@value = v
end
end
c1 = C.new(10)
# p c1.value()
p c1.getValue()
# p c1.value = 20
c1.setValue(20)
c1.show()
- 위처럼 지원이 불가능한 기능을 대체하기 위해, 관습적으로 인스턴스 변수의 값을 읽거나 쓰는 내용을 의미하는 이름인
getValue
,setValue
가 있다.
4) Python 인스턴스 변수 읽고 쓰기를 위한 get ,set 메서드
class C:
def __init__(self, v):
self.value = v
def show(self):
print(self.value)
def getValue(self):
return self.value
def setValue(self, v):
self.value = v
c1 = C(10)
print(c1.getValue())
c1.setValue(20)
print(c1.getValue())
- Python 에서 또한
getValue
,setValue
로 메서드를 지정하여 사용가능하다. - python은 직접 간접 모두되는데 일반적으로 굳이 메서드를 지정해서 사용하는 경우가 많다. 왜 그럴까?
5) get, set을 사용하는 이유
- python
class Cal(object):
def __init__(self, v1, v2): # 생성자에 유입되는 값도 미리 방지
if isinstance(v1, int):
self.v1 = v1
if isinstance(v2, int):
self.v2 = v2
def add(self):
return self.v1+self.v2
def subtract(self):
return self.v1-self.v2
def setV1(self, v):
if isinstance(v, int): # isinstance : 첫번째 인자가 두번째 인자의 인스턴스라면 true를 아니면 false를 return
self.v1 = v # false가 되면서 실행이 되지 않고 패스 -> 방지 가능
def getV1(self):
return self.v1
c1 = Cal(10, 10)
print(c1.add())
print(c1.subtract())
# c1.v1 = 'one'
# c1.v2 = 30
c1.setV1 = 'one'
c1.getV1 = 30
print(c1.add()) # type 오류가 난다. 문자와 숫사 합이 안됨
print(c1.subtract())
# 입력값이 숫자이길 바람 -> 하지만 입력자는 그걸 모르고 언제든지 문자를 기입가능함 -> 중대한 문제가 생김
- set, get을 통한 인스턴스 변수의 값을 바꾸거나 하는 경우 개발자가 원하는 입력값을 넣기를 기대하는건 옳지 않다.
- integer 정수 숫자 값을 넣길 바라지만, 'one'과 같은 문자열을 입력받을수도 있다. 그러면 에러가 생기고 그것은 큰 문제를 초래한다.
- 그래서
if
조건문과isinstance()
함수를 사용하여 입력 받는 문자가 잘 못되었을 때 그냥 통과하는 조건을 넣는다. isinstance(v1, int)
: 첫번째 인자가 두번째 인자의 인스턴스 이냐 ? -> 맞으면 True , 틀리면 False를 내 놓음-> false일 경우 실행하지 않고 pass함- 이를 통해 에러를 예방하고자 한다.
- Ruby
class Cal
def initialize(v1,v2)
# p v1, v2
@v1 = v1
@v2 = v2
end
def add()
return @v1+@v2
end
def subtract()
return @v1-@v2
end
def setV1(v)
if v.is_a?(Integer) # v.is_a? : v에 담겨 있는 어떤 인스턴스가 integer 인지?
@v1 = v
end
end
def getV1()
return @v1
end
end
c1 = Cal.new(10,10)
p c1.add()
p c1.subtract()
c1.setV1(20)
p c1.add()
c1.setV1('one')
p c1.add()
- Ruby의 경우에도 이를 방지하고자
v.is_a?(Integer)
을 사용한다. v.is_a?
: v에 담겨 있는 어떤 인스턴스가 integer 인지? -> true, false
하지만 루비와 파이썬은 인스턴스 변수에 직접 간접 접근이 다르기 때문에 생각해 볼수 있다. 결국엔 파이썬은 인스턴스 변수에 직접 접근하는 것은 막을 수 없다.
python은 인스턴스 변수에 직접 접근이 가능하기 때문에 에러가 날수 있어 입력자가 올바르게 사용할것이라고 전제하고 만든것 같고 (자유도를 줌)
ruby는 이것 마저 통제하여 get,set을 사용해서 에러를 최소화 시키고자 하는 느낌이다. (엄격함)
그런데 이렇게 set,get메서드를 계속 지정하기란 쉽지 않기에 통합 개발환경IDE에서는 자동으로 이를 만들어준다.
'Python' 카테고리의 다른 글
20210128_Python 와 Ruby 비교05 - 객체와 모듈, 다중상속, mixin, 패키지 매니저 (0) | 2021.01.28 |
---|---|
20210127_Python 와 Ruby 비교04 - 인스턴스 변수 접근, 클래스 변수(클래스 멤버), 오버라이딩 (0) | 2021.01.27 |
20210122_Python 와 Ruby 비교02 - Ruby 함수, 블럭, 모듈 (0) | 2021.01.23 |
20210121_Python 와 Ruby 비교 01 - Ruby 위주로 (0) | 2021.01.21 |
20210113_ Python 입문10(총 활용), 간단한 타이핑 게임 (0) | 2021.01.13 |