ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Ruby VS Python 뭐가 다를까? [2부]
    💻 프로그래밍/Ruby On Rails 2021. 3. 13. 15:25

    안녕하세요! 이번에는 Ruby VS Python 비교 두 번째 포스팅입니다.

    이번 포스팅에서는 method, class, exception(예외처리) 등 에 대해 비교해보고 느낀 점을 공유해보겠습니다.

     

     

    👨‍💻 문법 비교


    1. method (함수)

    - Ruby♦️

    def test_method(temp = "default")
        return "hello world! #{temp}"
    end
     
    puts test_method
    puts test_method('무야호!')
    puts test_method '무야호~'
    cs

    터미널 출력 화면

    - Python🐍

    def test_method(temp="default"):
        return f"hello world! {temp}"
     
     
    print(test_method())
    print(test_method('무야호!'))
     
    cs

    터미널 출력화면

    일단은 함수의 선언도 크게 다르지는 않은 것 같아요. Ruby는 특이한 게 파라미터를 함수에 전달할 때 괄호를 사용하지 않아도 되더군요. 역시 Ruby는 최대한 간결하게 사용하려는 게 문법에서 많이 드러나는 것 같습니다.

     

    2. class (클래스)

    - Ruby♦️

    class Company
        attr_writer :name # 인스턴스 변수에 수정하기 위해 사용
        attr_reader :name # 인스턴스 변수를 접근하기 위해사용
        attr_accessor :sector #인스턴스 변수에 접근하고 수정하기 위해 사용
        @@count = 0
        
        def initialize(name, sector)
            @name = name
            @sector = sector
            @@count += 1 # 회사 카운트
        end
        
        def describe
            puts "저희 회사의 이름은 #{@name} 이며, 업종은 #{@sector} 입니다"
        end
        
        def self.print_intro # 클래스 메소드 (인스턴스 선언을 하지 않아도 사용가능)
            puts "이것은 Company class 입니다"
        end
        
        def self.get_company_count
            return @@count
        end
    end
     
    Company.print_intro # 클래스 메소드 호출 (인스턴스 선언 없이 호출 가능)
    jay_company = Company.new('jay''game')
    jay_company.describe
    jay_company.name = 'new_jay'
    jay_company.describe
     
    zzeri_company = Company.new('jay''game')
    puts Company.get_company_count
    cs

    실행결과

    - Python🐍

    # Ruby에서도 클래스의 부모 클래스는 object Python에서는 명시해주는 걸 선혼
    # class Company(object):
    class Company:
        count: int = 0 # 클래스 변수
     
        def __init__(self, name, sector): ## __xxx__ 매직 메서드에서 사용되는 컨벤션
            self.name = name # 인스턴스 변수(self)
            self.sector = sector # 인스턴스 변수(self)
            Company.count += 1
     
        def describe(self):
            print(f"저희 회사의 이름은 {self.name} 이며, 업종은 {self.sector} 입니다")
     
        @classmethod # 클래스 메소드 선언 데코레이터
        def print_intro(cls):
            print("이것은 Company class 입니다")
     
        @classmethod
        def get_company_count(cls):
            return cls.count
     
     
    Company.print_intro() # 클래스 메소드 호출 (인스턴스 선언 없이 호출 가능)
    jay_company = Company('jay''game')
    jay_company.describe()
    jay_company.name = 'new_jay'
    jay_company.describe()
     
    zzeri_company = Company('jay''game')
    print(Company.get_company_count())
    cs

    실행결과

    class 선언은 두 언어가 많이 다른 것 같습니다. 일단 첫 번째로는 class변수, instance 변수의 선언입니다. 

    python의 경우 self로 선언한 변수는 인스턴스 변수, class 내부에서 선언된 변수는 class변수입니다.

    ruby는 반면에 @변수가 instance 변수, @@변수는 class 변수입니다. 그리고 rubyattr_writer, attr_reader, attr_accessor 같은 걸로 인스턴스 변수 접근에 대한 권한을 줄 수도 있습니다.

     

    또 한 가지 다른 점은 class method의 경우 python에서는 @classmethod로 선언을 하게 되는데, ruby는 함수 앞에 self를 붙이는 게 class method의 선언입니다. class method는 별도의 instatnce 생성 없이 사용 가능합니다.

     

    두 언어가 기능은 비슷한데, 선언하는 방법과 문법의 차이가 확실히 다르네요. python만 사용하다가 ruby를 보니까 살짝 헷갈리기도 하네요:D

    Ruby에서 instance에서 class method를 호출하려고 할때 오류


    3. class (클래스) 상속
    -
     Ruby♦️

    # 클래스 상속
    class Animal
        def bark
            puts "부모 클래스"
        end
        
        private # private 선언하면 외부 접근 불가
        def age
        end
        
        public # 메소드는 기본적으로 public
        def name
        end
    end
     
    class Dog < Animal
        def bark
            super()
            puts "멍멍"
        end
     
    end
     
    dog = Dog.new()
    dog.bark
    puts Dog.superclass # 부모클래스 확인
    dog.name
    cs

    실행결과

    - Python🐍

    from abc import abstractmethod, ABCMeta
     
     
    class Animal(metaclass=ABCMeta): # 추상 메소드 구현을 강제하기 위해
        @abstractmethod # 이 데코레이터를 붙이면, 상속받는 클래스에서 오버라이딩을 꼭 해줘야한다
        def bark(self):
            print("부모 클래스")
     
        def __age(self): #  __(언더스코어 2개)가 private이라는 컨벤션
            pass
     
        def name(self):
            pass
     
     
    class Dog(Animal):
        def bark(self):
            super().bark()
            print("멍멍")
     
     
    dog = Dog()
    dog.bark()
    print(Dog.__bases__)
    dog.name
     
     
    cs

    실행결과

    class의 상속도 두 언어 비슷하나 private, public 선언이 살짝 다릅니다. python은 __(언더스코어 2개)로 private이라는 표현을 컨벤션으로 할 수 있고, ruby의 경우 private이라고 함수 위에 데코레이터(?)처럼 선언을 해줘야 한다. (실제로 이게 데코레이터 같은 역할인지는 좀 더 찾아봐야 할 것 같습니다.)

     

    추가로 python에서는 metaclass=ABCMeta를 선언하고 메서드에 @abstractmethod 데코레이터를 사용하면, 상속받는 자식 클래스에 오버라이등을 강제할 수 있습니다. ruby에도 이런 데코레이터가 있는지 찾아봐야겠네요.

     

    4. 싱클톤 메서드

    - Ruby♦️

    class Foo
    end
     
    new_foo = Foo.new()
    new_foo2 = Foo.new()
     
    def new_foo.print_hello
        puts "hello!"
    end
     
    new_foo.print_hello
    new_foo2.print_hello
    cs

    실행결과

    ruby는 class에서 정의한 메서드 이외에 추가로 인스턴스 고유의 메서드를 가질 수 있습니다.

     

     

    5. 예외처리

    위에서 선언한 Dog 클래스를 사용해서 예외처리에 대해 알아보겠습니다.

    - Ruby♦️

    dog = Dog.new()
     
     
    begin
        dog.age # private method erroed
    rescue => e 
        # 예외 발생
        puts "------------------" 
        puts "Error : #{e.class}" #standard Error
        puts "Error : #{e.message}" # 에러 메세지
        puts "Error : #{e.backtrace}" # 콜스택 배열
        puts "------------------"
    else
        # 예외가 발생하지 않은 경우 실행
        puts "예외 발생되지 않음"
    ensure 
        # 예외 유무 상관없이 실행
        puts "무야호!"
    end
    cs

    실행결과

    - Python🐍

    dog = Dog()
     
    try:
        dog.age() # private method erroed
    except Exception as e:
        # 예외 발생
        print("------------------")
        print(f"Error : {e.__class__}"#standard Error
        print(f"Error : {e.__str__()}"# 에러 메세지
        print(f"Error : {e.__traceback__.tb_frame}"# 콜스택 배열
        print("------------------")
    else:
        # 예외가 발생하지 않은 경우 실행
        print("예외 발생하지 않음")
    finally:
        # 예외 유무 상관없이 실행
        print("무야호!")
    cs

    실행결과

    python, ruby 두 언어 모두 예외처리를 똑같이 할 수 있습니다. 다만, 문법적 차이는 있습니다. 신기하게 ruby에서는 exceptrescue라고 하더라고요. 예외처리를 구출(?)하라는 의미인가 ㅋㅋㅋ 다른 언어에서도 저렇게 사용하는지 확인해보는 것 도 재밌겠네요.

     

    그 외 다른 점은 ensure, finally가 다르네요.

     

    6. yield
    -
     Ruby♦️

    # yield
    def check_yield
        if block_given?
            yield puts "block"
        else
            puts "no block"
        end
    end
     
    chekc_yield 
    chekc_yield {"block"}
     
    puts "------------------------"
     
    #파라미터가 있는 yield 
    def check_yield_with_param(item) 
        puts "before yield" 
        yield(item) 
        puts "after yield" 
    end 
     
    check_yield_with_param("block") { |item| puts "#{item}" }
     
    cs

    실행결과

    - Python🐍

    def check_yield():
        yield print("block")
        return '더이상 호출 할게 없음'
     
     
    try:
        check_yield = check_yield()
        next(check_yield)
        next(check_yield)
    except StopIteration as e:
        print(e)
     
    cs

    실행결과

    yield 키워드를 python을 사용하면서 많이 사용해보진 않았는데요. 제너레이터를 만들 때 사용합니다.  둘 다 비슷하긴 한데 ruby의 경우 내부에 yield가 있는 경우 block을 공급해야 한다고 합니다. 그리고 block_given? 메서드로 block이 공급되었는지도 확인 가능합니다.

    # yield
    def check_yield
        if block_given?
            yield puts "block"
        end
        yield 
    end    
     
    check_yield 
     
    cs

    block을 전달하지 않은 경우

    block을 전달하지 않고 yield를 만나게 되면 이렇게 오류가 나옵니다.

     

     

    👋 마치며


    이번 포스팅에서는 python, ruby의 함수, 클래스, 상속, 예외처리 등에 대해 비교해봤습니다. 좀 더 깊게 파고들면 더 많은 내용을 알아야 하지만, 일단 가볍게 비교해 보기만 했습니다.

     

    python, ruby를 비교해보면서 두 언어가 비슷한 점도 많고 다른 점도 있다고 느꼈습니다. 가장 크게 느낀 점은 python도 매우 간결하고 읽기 쉬운 코드를 지향하지만 ruby는 좀 더 간결한 문법을 지향하는 것 같습니다. 

     

    특히 user_score.select { |k| k.size == 4 } 같은 select와 익명 함수를 이용한 문법은 python보다 더 간결하게 로직을 구현할 수 있다는 점이 인상 깊었습니다.

     

    그럼 오늘도 좋은 하루 보내시길 바랍니다!

    댓글

운동하는 개발자 JAY-JI