믹스인(Mixin)



앞서 파이썬에서 다중 상속에 대해 살펴보았다.

다중 상속에서 루비에 대해 다루지 않은 이유는 루비는 다중상속을 지원하지 않기때문이다.

대신 믹스인이라는 세련된 방법을 사용한다. 믹스인이라는 것은 객체와 모듈의 관계이다.





1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# ruby
module M1
  def m1_m
    p "m1_m"
  end
end
module M2
  def m2_m
    p "m2_m"
  end
end
class C
  include M1, M2
end
= C.new()
c.m1_m()
c.m2_m()
cs


위의 코드를 찬찬히 읽어보면 단번에 어떤 부분이 믹스인인지 알수 있을 것이다. 바로 include라는 부분이다.

먼저 모듈 M1과 M2가 선언되며, 클래스 C가 이 모듈들을 include한다.

모든 과정이 끝난후에 C 클래스의 인스턴스를 만들어 주고, include한 두 모듈들의 메소드를 호출하면

에러없이 정상적으로 동작하게 된다.

따라서, C가 M1과 M2의 메소드를 포함하고 있는 것처럼 행동하는 것이 믹스인의 핵심 이다.



출처 : 생활코딩, 파이썬&루비


저작자 표시
신고

다중상속(Multiplex Inheritance)




기존에 부모 객체로 부터 메소드를 받을 수 있는 것을 상속이라고 하며,

여러개의 부모객체로 부터 상속을 받는 것을 다중 상속이라고 한다.

객체 지향을 하는 언어들이 모두 다중 상속을 지원 하는 것은 아니며, 대부분은 지원하지 않는다.

이유는 죽음의 다이아몬드라는 다중상속의 크나큰 단점때문인데,

이 때문인지 몰라도 루비에서는 다중상속을 지원하지 않고 파이썬에서는 지원한다.

대신 루비에서는 Mixin이라는 기능을 이용해서 비슷한 목적을 이룰수 있다.

따라서 이번에는 파이썬 코드만 보도록 하겠다.





1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# python
class C1():
    def c1_m(self):
        print("c1_m")
    def m(self):
        print("C1 m")
 
class C2():
    def c2_m(self):
        print("c2_m")
    def m(self):
        print("C2 m")
 
class C3(C2, C1):
    def m(self):
        print("C3 m")
 
= C3()
c.c1_m()
c.c2_m()
c.m()
print(C3.__mro__)
cs


C3는 C1과 C2로부터 상속을 받는다.

따라서 C3의 뒤쪽에 C1과 C2를 명시해줌으로써 상속을 받게 된다.

이로인해 기존에 부모객체가 가지고 있던 모든 메소드를 사용할 수있게되며,

C1이 가지고 있는 m이라는 메소드만 오버라이드 한 상태이다.

따라서 아래의 메소드 호출 코드 3줄은 모두 정상적으로 동작하게 된다.


이렇게 다중상속은 상당히 편리한 기능을 제공하고 있는듯 하지만 제한적인 환경에서만 유용하다.

실제로 규모가 커진 상태에서 다중 상속을 사용하게되며면, 알수 없는 여러가지 문제점이 발생하기때문에

다중상속을 사용하는 부분에서는 상당히 신중해야한다.

이때문에 일부 언어들이 다중상속을 지원하지 않는 것이다.


그렇다면 다중상속이 일으킬수 있는 복잡한 상황들이란 어떤것일까.

만약 부모 객체에서 같은 이름의 메소드를 동시에 가지고 있다면 어떤것을 호출해야하는지 생각해보자.

자신이 만든 부모 객체가 아니면 내부 구조가 어떻게 이루어 져있는지 모르기때문에 상황이 더욱 복잡해진다.

또한 상속받는 부모객체도 부모가 있다면 상황은 걷잡을수 없이 악화된다.

소프트웨어는 조금의 조작 만으로도 금방 복잡해지기 때문에 이와같은 상황은 자주 일어날수 있다.


부모 객체에서 같은 이름의 메소드를 호출하면, 앞쪽에 등장한 부모객체의 우선 순위가 높다.

만약 객체.__mro__라는 명령어를 호출하면 우선순위를 확인할 수 있다.




출처 : 생활코딩, 파이썬&루비



저작자 표시
신고

객체와 모듈




1
2
3
4
5
6
7
8
9
# test.py
import lib
obj = lib.A()
print(obj.a())
 
#lib.py
class A:
    def a(self):
        return 'a'
cs


이번에는 객체를 모듈화 하는 것에 대해 알아보도록 한다.

먼저 클래스에 대한 모듈 파일을 만든다.

이렇게 만들어진 모듈 파일을 사용할 파일에서 import 시켜야한다.

그리고 나서 모듈 내부의 객체 이름으로 인스턴스를 만들어 준다.

결과적으로, 해당 객체 인스턴스가 생성되고, 내부 메소드를 사용할 수 있게된다.





1
2
3
4
5
6
7
8
9
10
11
12
13
# test.rb
require_relative 'lib'
obj = Lib::A.new()
p obj.a()
 
#lib.rb
module Lib
  class A
    def a()
      return 'a'
    end
  end
end
cs


다음으로는 루비 코드이다.

루비도 마찬가지로 module 키워드를 통해 모듈을 생성해준다.

그리고 모듈을 사용할 파일의 위치에서 require 키워드를 통해 로드해 준다.

여기서 A라는 클래스를 가져오기 위해서는 ::(더블 콜론)을 사용해준다.

그리고 인스턴스를 만들어야 하므로 객체 뒤에 new 키워드로 인스턴스를 생성해준다.

루비 코드도 마찬가지로 모듈로 부터 객체를 불러온후 인스턴스가 생성되어 내부의 메소드를 사용할 수 있게된다.


이렇게 모듈로 객체를 분리하게 된다면, 파일 마다의 코드 길이가 짧아지고 가독성이 높아지며,

하나의 모듈을 여러개의 다른 파일에서 사용할 수 있으므로 재사용성이 높아진다.



출처 : 생활코딩, 파이썬&루비


저작자 표시
신고

오버라이드(Override)




오버라이드는 재정의라는 의미를 내포하고 있다.

상속이라는 개념에서 상당히 중요한 기능이며, 복잡해진 객체 지향을 좀 더 잘 사용하기 위해 만들진것이다.


오버라이드는 상속 받은 메소드를 재정의 하는 것을 말한다.

부모 객체에서 자식 객체로 메소드가 넘어갈 때 이 메소드의 기능을 수정 하기 위해

해당 메소드의 코드를 다시 작성하는데 이것이 오버라이드이다.





1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# python
class C1:
    def m(self):
        return 'parent'
class C2(C1):
    def m(self):
        return super().m() + ' child'
    pass
= C2()
print(o.m())
 
# ruby
class C1
  def m()
    return 'parent'
  end
end
class C2 < C1
  def m()
    return super()+' child'
  end
end
= C2.new()
p o.m()
cs


위의 코드는 오버라이드에 대한 예제이다.

먼저 파이썬을 보면, C2는 C1의 자식 객체이다. 즉, C1으로 부터 상속받은 객체이다.

C2 내부에 pass는 비어있는 객체이므로 에러를 내지않고 계속 실행시키라는 의미이다.

C2 에서는 부모객체로 부터 받은 m 메소드를 오버라이드한다.

m 메소드 내부의 super는 부모객체를 가리키며, 따라서 super().m()은 부모객체의 m 메소드를 호출하는 것이다.

결과적으로 실행시 parent child라는 문자열이 호출된다.


다음으로 루비 코드를 살펴 보자.

문법적으로 차이가 있지만 오버라이드의 사용방법은 파이썬과 차이가 나지 않는다.

결과적으로 오버라이드 후에도 파이썬 코드와 결과 값이 같다.

다만 차이가 나는것은 super 메소드인데, 루비에서는 super는 부모 객체를 가리키지 않고

super가 소속되어있는 메소드와 같은 부모객체의 메소드를 가리킨다.



출처 : 생활코딩, 파이썬&루비


저작자 표시
신고

클래스 멤버(Class Member)




1
2
3
4
5
6
7
# ruby
require 'date'
d1 = Date.new(200011)
d2 = Date.new(201011)
p d1.year()
p d2.year()
p Date.today()
cs


지금까지 배운 변수와 메소드는 '인스턴스 멤버'였다.

이번에 배울 내용은 '클래스'에 소속 되어있는 변수와 메소드이다.

그렇다면 인스턴스의 멤버와 클래스멤버의 차이점과,

왜 이런 차이점을 가지고 있는지에 대해 생각해보자.


먼저 루비에서 날짜에 대한 기능을 가지고 있는 객체를 살펴보자

require 로 date객체를 불러온 후 d1이라는 변수에 Date 객체를 이용해서 인스턴스를 만든다.

똑같은 방법으로 d2 변수에도 인스턴스를 만든다.


현재 d1에는 2000년 1월 1일 이라는 데이터를 가지고 있으며, d2에는 2010년 1월 1일 이라는 데이터가 있다.

이 상태에서 d1 인스턴스에 속해 있는 year라고 하는 메소드를 실행시키면 

이 메소드는 자신이 속해있는 d1이라는 인스턴스의 내부에 저장되어있는 년도의 값을 리턴한다.

이처럼 year라는 메소드는 d1에 속해있기때문에 내부에 소속되어있는 2000이라는 값을 리턴하는 것이다.

반대로 밑에 있는 year는 d2에 소속이므로 d2의 내부값인 2010을 화면에 출력하게된다.


여기서 중요한점은 year라는 메소드들은, 하는일은 같지만 각각의 메소드들이 누구의 소속이느냐에 따라서

그 소속된 인스턴스의 내부값을 사용해서 결과를 리턴한다는 것이다.

바로 그런점에 각각의 메소드는 각각의 인스턴스의 소속되어있다 라고 할수 있다.


이번에는 Date라는 클래스를 직접 지정하고 today라고 하는 메소드를 호출해서 출력해보자.

결과는 현재, 오늘의 값이 나오게 된다.

위에 있는 2개의 메소드와 밑에 있는 메소드의 가장 큰 차이는 두 메소드는 각각 인스턴스의 소속이었으나,

아래의 메소드는 클래스 소속이라는 것이다.





1
2
3
4
5
6
7
8
9
10
11
12
13
14
# ruby
class Cs
  def Cs.class_method()
    p "Class method"
  end
  def instance_method()
    p "Instance method"
  end
end
= Cs.new()
Cs.class_method()
i.instance_method()
#Cs.instance_method() 오류발생
#i.class_method() 오류발생
cs


이번에는 우리가 직접 클래스에 소속되어있는 멤버를 활용해서 코드를 작성하는 방법을 알아보자

먼저 루비 코드이다.

Cs라는 클래스를 선언하고 안에 메소드를 만들어준다.

위의 코드에서 클래스안에 2개의 메소드가 있는데 두 메소드의 차이점을 찾아보자.

첫번째 메소드는 메소드 이름 앞에 소속되어있는 클래스 이름이 붙어 있다.

이렇게 메소드 이름앞에 클래스 이름이 붙어 있으면 클래스 멤버가 된다.


따라서 아래에 해당 메소드를 호출할때, Cs.class_mathod와 같이 클래스 이름으로 호출해야하며

인스턴스 멤버는 해당 인스턴스로부터 호출해야한다.





1
2
3
4
5
6
7
8
9
10
11
12
13
14
# python
class Cs:
    @staticmethod
    def static_method():
        print("Static method")
    @classmethod
    def class_method(cls):
        print("Class method")
    def instance_method(self):
        print("Instance method")
= Cs()
Cs.static_method()
Cs.class_method()
i.instance_method()
cs


다음은 파이썬의 클래스 멤버 생성 예제이다.

파이썬은 루비와 다르게 클래스 멤버로 해당하는 메소드가 2가지 종류가있다.

 static 메소드와 class 메소드가 그것이다.


우선 인스턴스 메소드에는 첫번째 인자로 self가 들어가게 된다.

그리고 static 메소드와 class메소드를 설정하기 위해서 해당 메소드 위에 @ 장식자를 붙여준다.

이렇게 @staticmethod, @classmethod로 구분하게 된다.

또한 classmethod는 첫번째 인자로 cls를 가져야한다. 이 cls는 클래스를 가리킨다.


이와 같은 형식으로 static과 class를 선언할 수 있다.

지금 단계에서는 static과 class를 동일 하다고 생각하자.





1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# ruby
class Cs
  @@count = 0
  def initialize()
    @@count = @@count + 1
  end
  def Cs.getCount()
    return @@count
  end
end
i1 = Cs.new()
i2 = Cs.new()
i3 = Cs.new()
i4 = Cs.new()
p Cs.getCount()
cs


다음은 클래스 멤버 중에서 클래스 변수이다. 먼저 루비 코드이다.

위의 코드에서 바로 알수 있듯이 @@가 2개 붙어있으면 클래스 변수가 된다.

클래스 변수는 모든 인스턴스가 공유하는 변수이다.

따라서 인스턴스를 만들때마다 클래스 변수가 1씩 증가했으므로 최종적으로 3이 출력된다.


다시 말하자면 @가 1개 붙어있을때는 인스턴스변수이며, 인스턴스마다 생성되므로 모두 다른 값을 가질수 있으나

@@가 2개 붙어있는 것은 클래스에 소속된 클래스 변수이므로 모든 인스턴스에서 값을 공유하게 된다.





1
2
3
4
5
6
7
8
9
10
11
12
13
# python
class Cs:
    count = 0
    def __init__(self):
        Cs.count = Cs.count + 1
    @classmethod
    def getCount(cls):
        return Cs.count
i1 = Cs()
i2 = Cs()
i3 = Cs()
i4 = Cs()
print(Cs.getCount())
cs


이번에는 클래스 변수의 파이썬 버전이다.

클래스 변수를 만들기 위해서 Cs를 변수 앞에 붙여준다. 

이 때 주의할 점은 class 안, 메소드들의 밖에 있는 변수는 자동으로 클래스 변수로 선언되기때문에 

그 변수 앞에는 클래스 이름을 붙이면 안된다.

따라서 메소드 내부에서 클래스 변수에 접근할 때만 클래스 이름을 붙여준다.



출처 : 생활코딩, 파이썬&루비



저작자 표시
신고
  1. ruby_newbie 2017.06.29 15:26 신고

    본문의 Class Cs, ruby 변수에서 initialize가 4번 되었으니 4를 print하는 것이 맞지 않나요?

상속(Inheritance)



우선 비유를 통해 상속을 알아보자.

우리가 자전거를 만든다고 했을때, 부품들을 조합해서 만들게된다.

이렇게 사용되는 부품을 함수라고 생각해보자.


이렇게 함수라는 부품을 조합해서 자전거라는 객체를 만들었다.

그리고 이 자전거를 다른사람에게 팔았는데, 팔린 후 새로운 기능을 달고싶어했다.

결국, 자전거에 전조등을 달게 되었는데 기존에 깔끔하게 자전거 기능만 담고있던 자전거에

전조등의 기능을 추가하면서 새로운 객체가 되었다.


위의 예 처럼 새로운 기능을 추가해서 새로운 객체를 만드는 것.

이것을 상속(Inheritance)이라고한다.





1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#python
class Class1(object):
    def method1(self): return 'm1'
c1 = Class1()
print(c1, c1.method1())
 
class Class3(Class1):
    def method2(self): return 'm2'
c3 = Class3()
print(c3, c3.method1())
print(c3, c3.method2())
 
class Class2(object):
    def method1(self): return 'm1'
    def method2(self): return 'm2'
c2 = Class2()
print(c2, c2.method1())
print(c2, c2.method2())
cs


먼저 파이썬의 상속에 대해 알아보자.

첫번째로 일반적인 클래스인 Class1을 만든다.

이 클래스 내부에는 m1이라는 문자를 리턴하는 method1만 선언되어있다.

여기까지는 우리가 이때까지 배운 객체 생성이다.


다음으로 Class2는, 상속이라는 개념이 없을 경우 우리가 사용하는 방법이다.

Class2는 method1이 이미 Class1에 선언되어있더라도, 받아서 사용할수가 없기때문에 다시한번 선언해야한다.

결과적으로, 상속을 사용하지 않을 경우 프로그램의 코드가 길어지며 재활용성이 떨어지게 된다.

이로 인해 할수 있는것은 상속도 프로그래밍의 재활용성과 효율성을 추구하는 결과물이라는 것이다.


마지막으로 Class3는 Class1으로부터 상속받는 객체이다.

상속을 하기위해서는 우리가 객체를 만들기 위해서 적어주던 객체이름(Object) 양식에서

Object 자리에 상속 받을 객체 이름을 넣어주면 된다.

우리는 Class1을 상속받으므로 Class1의 이름을 넣어주자.

자바로 치자면 이 괄호안에 상속 받을 이름을 넣는것이 extends와 비슷하다.





1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# ruby
class Class1
    def method1()
        return 'm1'
    end
end
c1 = Class1.new()
p c1, c1.method1()
 
class Class3 < Class1
    def method2()
        return 'm2'
    end
end
 
c3 = Class3.new()
p c3, c3.method1()
p c3, c3.method2()
cs


다음은 루비이다.

파이썬으로 상속이 없을 경우를 살펴보았으니 바로 상속으로 넘어가도록 하겠다.

우선 위의 코드는 파이썬과 기능적으로는 동일하다.

루비에서 상속을 하기 위해서는 클래스 이름 뒤에 < 기호와 상속 받을 클래스의 이름을 입력하면된다.


파이썬과는 조금 다르지만 똑같이 동작하는데, 이는 객체지향을 기반으로 한 언어들이

문법적으로만 차이를 보이며, 개념적으로는 거의 동일하기 때문이다.




출처 : 생활코딩, 파이썬&루비




저작자 표시
신고

캡슐화(Encapsulation)



객체는 규모 있는 애플리케이션을 만드는 과정에서 필요한 기능이다.

따라서 일반적인 프로그래밍 과정에서 객체의 필요성에 대해 못 느낄 수도 있다.

객체는 부품과 같다. 이러한 객체를 조립해서 큰 프로젝트를 만들어 가는 것이다.

따라서 좋은 객체를 만드는 것은 좋은 부품을 만드는 것이며, 이를 위해서 여러가지 조건을 충족해야한다.

그 중에 하나가 인캡슐레이션, 즉 캡슐화이다.


캡슐화란 객체가 어떤 일을 하는지와 상관없이 견고한 케이스로 감싸듯 외부의 영향을 받지 않게 하는 것이다.

즉, 이러한 케이스, 캡슐을 씌우는 것은 외부로부터 영향을 받아 변경되지 않도록 하는 것이다.

함수나 변수도 캡슐로 감싼 예 중의 하나이다.

함수의 조건문 반복문 등도 함수라는 캡슐로 싼 것이나 다름없고, 이러한 함수를 다시 묶어서 모듈로 감싼것이다.

객체도 이와 같은 맥락에서 내부의 동작을 보호하기 위한 캡슐이라고 할 수있다.





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
26
# python
class C(object):
    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
class C
  def initialize(v)
    @value = v
  end
  def show()
    p @value
  end
end
c1 = C.new(10)
# p c1.value
# c1.value = 20
c1.show()
cs

객체에 이를 적용시키면 인스턴수 변수의 수정에 대해 캡슐화를 적용할 수 있다.

파이썬에서 인스턴스 변수는 인스턴스 외부에서 접근하여 수정 및 사용이 가능하다.

그러나 루비는 언어 자체에서 인스턴스 변수에 접근 및 수정을 불가능하게 막아 놓았다.

이렇게 된 이유는 무엇이며, 어떻게 수정 및 호출을 해야하는걸까.





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
26
27
28
29
30
31
32
33
34
35
36
# python
class C(object):
    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())
 
# ruby
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()
# c1.value = 20
c1.setValue(20)
p c1.getValue()
cs


다른 객체지향 언어, 예를 들어 자바에서 사용하듯이 setter/getter 메소드를 사용해야한다.

setter는 변수를 setting하는 함수이며, getter는 변수를 가져오는 함수이다.

이는 임의로 변수로 접근하여 수정을 할 경우 자체적으로 assertion이 실행되는 특징이 있다.

이 때문에 좀 더 오류의 가능성이 적어지며 효율적인 코드 작성이 가능해진다.


setter나 getter 함수는 어떠한 이름을 사용해도 상관이 없으나

일반적으로 set변수이름/get변수이름 으로 사용하게 된다.





1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 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):
            self.v1 = v
    def getV1(self):
        return self.v1
c1 = Cal(10,10)
print(c1.add())
print(c1.subtract())
c1.setV1('one')
c1.v2 = 30
print(c1.add())
print(c1.subtract())
 
cs


위의 코드는 파이썬에 대한 예제이다.

파이썬에서는 isinstance(변수, 데이터형)으로 변수의 설정을 규제할 수 있다.

즉, 생성자의 데이터 값을 검증하는 것이다.

이를 assertion 과정이라고 하며 다른 데이터형으로 바뀌어 오류가 생기는것을 방지할 수 있다.

set 메소드도 마찬가지로 이렇게 데이터 형을 판단한뒤 인스턴스 변수를 변경하게 된다.


파이썬은 setter/getter 함수와 함께 데이터에 직접 접근하는 것을 막지 못한다.

따라서 이와같은 활용 방법은 권장되는 사항이지만 반드시 사용하는게 좋다.

기능이 좋은 IDE의 경우에는 객체 생성시 set/get이 자동으로 생성된다.





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
26
27
28
29
30
#ruby
class Cal
  attr_reader :v1, :v2
  attr_writer :v1
  def initialize(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)
      @v1 = v
    end
  end
  def getV1()
    return @v1
  end
end
c1 = Cal.new(10,10)
p c1.add()
p c1.subtract()
c1.setV1('one')
c1.v1 = 20
p c1.add()
c1.getV1()
cs


다음은 루비로 넘어가서 예제코드를 살펴보자

변수.is_a?(데이터형) 의 형식으로 assertion이 가능하다.

만약 setter 함수에 옳지 못한 데이터형이 들어 올 경우 인스턴스 변수 변경이 무시된다.


루비는 setter/getter를 사용하지 않는 경우는 변수에 대한 호출이나 변경이 불가능하다.

예를 들어 getter만 제공한 경우에는 인스턴스 변수에 대한 변경이 불가능하게된다.

이는 루비가 파이썬보다 변수에 대한 정책이 엄격하기 때문이다.




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
26
# python
class C(object):
    def __init__(self, v):
        self.__value = v
    def show(self):
        print(self.__value)
c1 = C(10)
#print(c1.__value)
c1.show()
 
# ruby
class C
  #attr_reader :value
  #attr_writer :value
  attr_accessor :value
  def initialize(v)
    @value = v
  end
  def show()
    p @value
  end
end
c1 = C.new(10)
p c1.value
c1.value = 20
p c1.value
cs


그렇다면 파이썬에서는 어떻게 인스턴스 변수에 접근을 못하게 막을수 있을까?

반대로 루비에서는 어떻게 인스턴스 변수에 접근을 할 수 있을까?


파이썬에서는 변수의 이름앞에 __를 붙여주면 된다.

__는 _(언더바)가 2개 붙어있는 형태이다.

이렇게 언더바가 2개 붙어지면 외부에서 접근할수 없는 변수가 된다. 


위의 루비 코드에서 attr_reader는 변수를 읽기 가능으로 설정하는 키워드이다.

또한, attr_write는 변수를 쓰기 가능으로 설정하는 키워드이다.

마지막으로  수정/삭제가 가능해진다.


이렇게 외부에서 접근이 가능한 변수를 파이썬에서는 property, 루비에서는 attribute라고 한다.



출처 : 생활코딩, 파이썬&루비

저작자 표시
신고

객체(Object)



객체 지향 프로그래밍(Object Oriented Programming)이라는 말에서도 알수 있듯이

객체는 OOP의 기반이 되는 개념중의 하나이다.


함수만 묶어 놓았던 모듈과는 다르게, 객체에서는 변수와 함수를 묶어서 찍어낼수 있는 개념이다.

이렇게 객체를 기반으로 생성된 것을 인스턴스라고한다.

여기서는 객체에 대한 개념을 깊게 다루지는 않을 것이며, 사용 방법에 대해 알아보도록하겠다.





1
2
3
4
5
6
7
8
# python
class Cal(object):
   # 클래스 코드
 
#ruby
class Cal
   # 클래스 코드
end
cs


먼저 class 키워드를 사용해서 객체를 만들어 준다.

이때, 루비는 역시 end 키워드를 사용해서 객체 생성 코드를 닫아주며,

파이썬은 class뒤에 객체이름(object):를 적어 준다.





1
2
3
4
5
6
7
8
9
10
11
# python
class Cal(object):
    def __init__(self, v1, v2):
        print(v1, v2)
 
#ruby
class Cal
  def initialize(v1,v2)
    p v1, v2
  end
end
cs


객체를 생성할 때 내부에 생성자를 만들어준다.

생성자라는 것은 객체로 부터 인스턴스가 만들어질때 자동으로 실행되는 메소드를 생성자라고한다.

이 생성자의 이름은 미리 약속되어있으므로, 해당 이름으로 사용해야한다.

파이썬은 __init__이며, 루비는 initialize이다.


따라서, 이 생성자에 원하는 코드를 작성해 놓으면 인스턴스가 생성될 때 자동으로 실행된다.

위의 코드에서 파이썬과 루비의 생성자는 v1, v2를 출력하게 설정했다. 

다른점은, 파이썬은 생성자를 만들때 첫번째 인자로 self를 넣어주어야 한다는 것이다.

이 self에 대한 내용은 아래의 파이썬 코드에서 설명하도록 하겠다.





1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Cal
  def initialize(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()
p c1.subtract()
c2 = Cal.new(30,20)
p c2.add()
p c2.subtract()
 
cs


최종적으로 완성된 객체와, 인스턴스 생성, 메소드 사용 코드이다.

위의 코드는 루비로 작성되었다.


먼저, 생성자가 실행될 때 인자로 받아온 2개의 값을 인스턴스 변수에 저장하게된다.

인스턴스 변수를 사용하기 위해서는 변수 이름앞에 @ 기호를 붙여주자.

이렇게 인스턴스 변수가 따로 있기 때문에 모든 인스턴스가 같은 객체로부터 생성되었더라도 다른 값을 가질수 있다.

그리고 내부에 더하기, 빼기 메소드를 만들어준다.


이렇게 생성된 객체를 인스턴스화 하기 위해서는 '객체이름.new(인자)'의 형식으로 만들수 있다.

또한 '인스턴스.메소드이름'의 형식으로 메소드 호출이 가능하다.





1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Cal(object):
    def __init__(self, v1, v2):
        self.v1 = v1
        self.v2 = v2
 
    def add(self):
        return self.v1+self.v2
 
    def subtract(self):
        return self.v1-self.v2
 
 
c1 = Cal(10,10)
print(c1.add())
print(c1.subtract())
c2 = Cal(30,20)
print(c2.add())
print(c2.subtract())
cs


이번에는 같은 기능을 하는 파이썬 예제이다.

이 때 루비에서 사용하는 @를 사용할수 없고 self를 사용해야되는데,

파이썬에서 사용하는 모든 메소드에서는 첫번째 매개변수로 self를 적용해야한다.

이 self는 해당 인스턴스를 가리키는 값이다. 타 언어의 this라고 생각해도 좋을듯하다.


따라서, 이 첫번째 매개변수로 해당 인스턴스의 변수를 사용하거나 지정 수 있다.

물론 이 self 인자는 다른 이름으로 바꾸어도 되지만 관습적으로 self라고 사용한다.



출처 : 생활코딩, 파이썬&루비


저작자 표시
신고

모듈(Module)



모듈이라고 하는 것은 프로그래밍에서 사용하는 부품이다.

어떻게 보면 함수도 부품이라고 할수 있는데, 함수보다 더 큰 규모의 파일이 모듈이 된다.

예를 들어, 학교에서 학생들을 가르치다가 학생들이 늘어남에 따라 학년으로 분리한다.

그러다가 학년이 감당할수 없을 만큼 규모가 증가하면 반이라는 단위로 나뉘게 된다.

만약 학년과 반의 개념이 없고 학교만 있다면, 학생들이 무질서하게 분포되어있으므로

문제가 생기게 된다.


학년은 교육 정도나 나이에 따라 분류되어지고, 반은 생일이나 키, 이름에 기반한 번호로 줄세워진다.

이와 같이 일정한 기준에 따라 성격이 비슷한 파일들을 하나의 디렉토리에 묶어준다.

따라서 모듈은 코드의 복잡도를 낮추고 문제를 방지하기 위한 중요한 기능이다.





1
2
3
4
5
6
7
8
# python
import math
print(math.ceil(2.9))
print(math.floor(2.9))
print(math.sqrt(16))
 
#ruby
puts(Math.sqrt(16))
cs


사실 우리는 모듈에 대한 것을 사용해 보았다.

수와 계산 포스팅에서, import math를 사용했는데, import는 모듈을 호출하는 명령어이다.

이 math 모듈에는 ceil(올림), floor(내림), sqrt(제곱) 등의 함수가 들어있다.

루비에서는 Math의 경우는, 별도의 import 없이 바로 Math를 호출해 주면된다.


따라서 이런 내장 모듈을 사용하면 코드를 작성하지 않고 손쉽게 문제를 해결할 수 있다.

반대로 말하자면, 이러한 모듈이 없다면 직접 모든 함수를 짜야하거나, 코드가 엄청나게 길어질수 있음을 의미한다.

그리고 코드를 한 파일에 넣어서 엄청나게 길어질 경우 변수나 함수의 이름이 충돌될 가능성이 있다.





모듈의 사용 - 파이썬


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# module1.py
def a():
    return 'a'
def b():
    return 'b'
def c():
    return 'c'
 
# module2.py
def a():
    return 'B'
 
#test.py
from module1 import a as z
import module2 as k
print(z())
print(k.a())
cs


파이썬의 모듈 사용 예제이다.

우선 module1과 2를 생성한다. 이 때, a라는 함수의 이름이 중복되는것에 주목하자.

test.py에서 이렇게 만들어진 모듈을 불러오게 되는데, 

우선 첫번째줄의 코드는 module1의 a라는 함수를 z로 사용하겠다는 의미이다.


여기서 사용된 as는 alias로, 가명이나 별명이라는 뜻이다.

같은 느낌으로 두번째 줄은, module2를 k라고 사용하겠다는 뜻이다.

이렇게 as를 사용함으로써 긴 모듈의 이름을 축약할 수 있고, 편리한 사용이 가능하다.


앞서 설정한 값을 기반으로 아래에 z()는 module1의 a함수를 사용한다는 뜻이며,

k.a()는 module2의 a함수를 호출하는 의미이다.





모듈의 사용 - 루비


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# module1.py
module Module1
  module_function()
  def a()
    return 'a'
  end
end
 
# module2.py
module Module2
  module_function()
  def a()
    return 'B'
  end
end
 
#test.py
require_relative 'Module1'
require_relative 'Module2'
puts(module1.a())
puts(module2.a())
cs


이제 루비에 대한 예제이다.

소스코드를 보면 알수 있듯이 모듈에서 부터 루비와 파이썬의 차이점이 생기기 시작한다.


모듈을 생성해주는 것은 module과  end 키워드를 사용한다.

루비에서는 모듈 이름은 대문자로 시작하는 것을 권장한다.


그리고 모듈 내부에 module_function()을 입력해야한다.

module안에 있는 모든 함수들을 모듈의 함수로 만들어 준다는 것이다.

다시말해, '모듈.함수이름'의 형식으로 접근이 가능하게 만들어주는 함수이다.


이렇게 만들어진 모듈을 사용하기 위해 test 코드에서 require_relative로 모듈을 호출한다.

그리고 '모듈.함수이름'을 통해 해당 모듈을 호출할 수 있다.

(require_relative는 호출하는 파일의 위치에 상대적으로 경로를 지정하게 된다.)



출처 : 생활코딩, 파이썬&루비



저작자 표시
신고

함수(Function)




1
2
3
4
5
6
7
8
9
10
# python
def func():
    print('Hi!')
func()
 
# ruby
def func()
    puts('Hi!')
end
func()
cs


중복의 제거와 재사용성, 코드의 효율성에 기반하여 프로그래밍은 발전해 왔다.

그 중에서도 함수는 재사용성을 높이고 효율적인 코드를 만들기위해 꼭 필요한 존재이다.


함수의 기본 개념은 입력값이 있으면 출력 값을 주는 코드이다.

물론 이 입력값의 개수는 0개부터 여러개가 될 수도 있다.

우리가 이때까지 사용하던 print도 함수의 종류이다.

print나 len등은 언어에서 제공해주는 내장함수이며, 별도의 설치나 조작이 없어도 사용이 가능하다.


사용자 지정 함수를 만들수도 있다.

위의 코드가 지정 함수에 대한 예제인데, 그렇게 효율적인 코드는 아니다.

def로 함수를 정의 하고, 함수명 뒤 괄호에 인자를 넣는다. 그리고 내부에는 함수의 동작 코드를 명시한다.

루비에서는 코드 블록의 마지막을 end로 감싸줘야한다.


이렇게 만들어진 함수를 사용하기 위해서는 함수이름 뒤에 괄호와 인자를 넣어주면 된다.

만들어진 func 함수를 실행하면 'Hi!'가 출력된다.

예시의 함수는 입력값과 출력값이 없다.





1
2
3
4
5
6
7
8
9
10
# python
def func():
    return 'Hi!'
print(func())
 
# ruby
def func()
    return 'Hi!'
end
puts(func())
cs


그렇다면 출력값을 만들어 주기위해서 어떻게 해야할까.

함수 내부에서 모든 코드를 다 실행하고 출력할 값을 반환해 주면된다.

이러한 반환은 return이라는 키워드로 이루어지며, 함수를 호출한 곳으로 값이 전달된다.


따라서 위의 코드에서는 함수를 호출한 곳으로 'Hi!'문자열이 반환되며,

이를 출력 했을 때 정상적으로 문자열이 나오게 된다.





1
2
3
4
5
6
7
8
9
10
# python
def Func(num):
    return 'Func'*num
print(Func(3))
 
# ruby
def Func(num)
    return 'Func'*num
end
puts(Func(3))
cs


입력값의 경우 앞서 언급했듯이, 함수 이름 뒤에 괄호 안에 넣어주면 된다.

함수 내부에서는 이렇게 전달된 입력값을 변수처럼 마음대로 사용이 가능하다.

내부에서 지역 변수로 선언해 준것이라고 생각하면된다.


주의해야 할 점은 숫자형이나 문자형 등은 아무리 함수 내부에서 값을 바꾸어도

외부에 값을 전달해준 변수는 변하지 않는다는 것이다.

이는 인자값의 복제가 일어나기 때문이다.





1
2
3
4
5
6
7
8
9
10
# python
def make_string(str, num):
    return str*num
print(make_string('b'3))
 
#ruby
def make_string(str, num)
    return str*num
end
puts(make_string('b'3))
cs


또한 인자를 전달하는데 있어서 위와 같이 여러개를 전달할 수가 있다.

이 또한 하나를 전달한 것 처럼 함수 내부에서 지역 변수처럼 사용이 가능하다.

대신 이렇게 전달되는 인자의 값이 많아지면 함수의 사용이 까다롭기 때문에

함수 내부에서 선언할 수 있는 변수는 내부에서 선언하고, 꼭 받아야 하는 값만 인자로 전달 받도록 하자.




출처 : 생활코딩, 파이썬&루비


저작자 표시
신고

+ Recent posts

티스토리 툴바