20210123_Python 와 Ruby 비교03 -
Python vs Ruby (루비 위주)
객체 지향 프로그래밍01 (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에서는 자동으로 이를 만들어준다.