💻 프로그래밍/Ruby On Rails

Ruby VS Python 뭐가 다를까? [2부]

피트웨어 제이 (FitwareJay) 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보다 더 간결하게 로직을 구현할 수 있다는 점이 인상 깊었습니다.

 

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