본문 바로가기

Python

Python OOP - Part2

앞선 내용에 이어 이번에는 OOP의 4원칙과 함께 OOP에 대해서 더 알아보도록 하겠습니다. 다만, 추상화의 경우 앞선 Part 1에서 이미 설명했으니 이번에는 나머지 3개( 상속, 다형성, 캡슐화 ) 에 대해서만 알아보도록 하겠습니다! 시작하겠습니다 :)

상속( Inheritance )


우선 상속이란, 클래스간의 계층관계를 형성하는 것으로, 상속해주는 대상인 부모 클래스가 상속받는 대상인 자식 클래스에 상속을 해줄 때 모든 메소드와 속성을 그대로 상속, 전달 해주는 것이다. 그렇다면 이러한 상속을 하는 이유는 뭘까? 당연히 다양한 이유가 있겠지만, 대표적인 이유로는 부모 클래스의 속성과 메소드를 그대로 유지하고 싶거나 대부분 유지하면서 일부를 변경하고 싶기 때문이다. 즉, 상속을 통해 반복은 엄청나게 줄여줄 수 있다는 것이다.

앞서 사용했던 예시를 그대로 가져와서 살펴보도록 하자.

class Robot:
    """
    [Robot class]
    Author : 이효석
    Role: test
    """
    
    population = 0 
   
    def __init__(self,name):
        self.name = name
        Robot.population +=1
    
    def say_hi(self):
        # code..
        print(f"Greetings, my masters call me{self.name}.")
        
    def cal_add(self,a,b):
        return a+b
    
    def die(self):
        
        print(f"{self.name}is being destroyed!")
        Robot.population-=1
        if Robot.population ==0:
            print(f"{self.name}was the last one.")
        else:
            print(f"There are still {Robot.population}robots working")
        

    @classmethod
    def how_many(cls):
        print(f"We have {cls.population} robots.")
        
    # staticmethod
    @staticmethod
    def are_you_robot():
        print("yes!!")
        
    def __str__(self):
        return f"{self.name}robot!!"
    
    def __call__(self):
        print("call!!")
        return f"{self.name} call!!"
        
        
#### 상속 ####

class Siri(Robot):
    pass 


siri = Siri()
print(siri) 
# ❌

siri = Siri('iphone8')
print(siri)
>>> iphone8robot!!
# ⭕

print(siri.are_you_robot())
>>> yes!!
print(siri.cal_add(3,4))
>>> 7

앞서 정의했던 위와 같은 Robot ClassSiri라는 클래스를 통해 상속받아올 수 있는 것이다. 그리고 이렇게 부모 클래스인 Robot이 갖는 모든 메소드와 속성이 자식클래스에 그대로 전달, 상속되어 자식클래스에서도 동일하게 사용할 수 있다는 것이다. 이렇게 모든 것이 상속되기에 앞서 Robot class에서 생성자에 인자를 받고 있기에 이를 상속한 Siri에서도 생성자에 인자를 받아야한다. 

pass
Python에서 pass 문법을 사용하는 이유는 뭘까? 간단하다. 그냥 넘어가기 위해서다. 그렇다면 왜 그냥 넘어가야할까? 파이썬은 아무것도 적지 않고 싶을 때 아무것도 적지 않는다면 SyntaxError : unexpected EOF while parsing 와 같은 Error message를 뱉기 때문이다. 따라서 pass는 빈 한줄과 동일하게 이해하고 사용하면 된다.

이렇게 상속에 대해 간단히 알아봤다. 그런데, 앞서 이야기했듯 우리가 상속을 하는 이유는 기존 것을 유지할 뿐만 아니라 새로운 것을 추가하거나, 기존의 것을 조금 수정해서 재사용하는 등의 목적이 있다. 이 두가지에 대해서 더 살펴보자.

자식클래스에 새로운 메소드나 속성 추가

class Siri(Robot):
    def call_me(self):
        print("네?")
        
    def cal_mul(self,a,b):
        self.a = a
        return a*b
    
    @classmethod
    def hello_apple(cls):
        print(f"{cls}")

siri = Siri("iphone8")
siri.call_me()
>>> 네?
print(siri.cal_mul(7,8))
>>> 56
Siri.hello_apple() 
>>> <class '__main__.Siri'>
print(siri.a)
>>> 7

이렇게 자식클래스에 자식클래스만의 속성 행위를 추가할 수 있다. ( 인스턴스 메소드,변수 그리고 클래스 메소드,변수 , static method 까지 모두 추가가능한 것이다. ) 그런데 여기서 한가지 체크해야할 점이 있다. 이렇게 상속받은 자식 클래스에서 cls는 어떤 클래스를 가리키는 걸까? 부모클래스를 가리키는 걸까? 자식클래스를 가리키는 걸까? 정답은 자식클래스이다. 여기서 지금까지 미뤄왔던 static method와 class method간의 차이점에 대해서 살펴볼 수 있다.

static method VS class method
둘간의 차이점은 상속을 할 때 비로소 나타난다. 간단히 static method는 clsself를 사용하지 않고 즉, 이러한 객체정보가 필요없는 함수를 만들 때 주로 사용한다고 한다. 위의 예제를 조금 수정해서 살펴보도록 하자. 
class Robot:
    
    default_name = "robot"
    
    def __init__(self):
    	self.call = self.default_name + "입니다"
        
    @classmethod
    def cls_name(cls):
    	return cls()
    
    @staticmethod
    def static_name():
    	return Robot()
     
    def calling(self):
    	print(self.call)
        
class Siri(Robot):
 	default_name = "Siri"
    
static = Siri.static_name()
cls = Siri.cls_name()

stat.calling()
>>> robot입니다

cls.calling()
>>> Siri입니다​

 

static method를 통해서는 부모클래스의 default name에 접근하지만, class method를 통해 접근하는 cls는 자식클래스 자체이기에 상속된 Siri라는 클래스에서 상속받은 static method와 class method 각각의 접근하게 될 경우 static method는 부모 클래스에 접근가능하지만, (자체를 불러서 ) class method의 cls를 통해서는 자식클래스 Siri 자체에만 접근가능하다는 것을 명확히 확인할 수 있다.

Method Overriding

메소드 오버라이딩을 통해 부모 클래스의 속성이나 행위를 변경할 수 있다. 하지만, 이에 앞서 override가 어떤 의미인지를 살펴봐야한다. "무효로 하다(nullify) 또는 무시하다와 같은 뜻으로도 사용되며 앞에 set되어 온 상태를 새로운 커맨드에 따라 바꾸든지 어떤 일련의 옵션 지정을 무시하여 새롭게 지정한 내용을 우선시키는 것." 네이버 컴퓨터사전의 정의는 이와 같다. override의 영어 그대로의 뜻 중 우선시하다라는 뜻이 있다. 여기서 사용되는 의미는 이것이다. 즉 부모 클래스의 어떤 메소드를 무시하고 이번에 새롭게 정의한 동일한 name을 가지는 메소드를 우선시하라는 의미인 것이다. 따라서, 아래와 같이 기존의 부모 클래스의 메소드가 수정가능한 것이다.

class Siri(Robot):
    def __init__(self,name,age):
        self.name = name
        self.age = age
        Siri.population +=1
           
    def call_me(self):
        print("네?")
        
    def cal_mul(self,a,b):
        self.a = a 
        return a*b
    
    @classmethod
    def hello_apple(cls):
        print(f"{cls}")
        
    def say_hi(self):
        print(f"Greetings, my masters call me{self.name}.by apple.")
        
    @classmethod
    def how_many(cls): 
        print(f"We have {cls.population} robots.by apple.")
        
    
siri = Siri("iphone8",17)

siri.say_hi()
>>> Greetings, my masters call meiphone8.by apple.
siri.how_many()
>>> We have 4 robots.by apple.

여기서 내가 overriding 한 것들 중에 say_hi라는 인스턴스 메소드를 살펴보자. 앞선 부모 클래스의 say_hi라는 메소드로 출력되는 print문 뒤에 .by apple을 붙이는 수정을 하고 싶었고 이를 위해서 메소드를 오버라이딩했다. 즉, 우리가 자식 클래스 Siri에서 새롭게 정의한 say_hi가 충돌시 우선시 되었다는 것이다. 물론 클래스 메소드 how_many 또한 마찬가지로 overriding될 것이다. 

그리고 __init__ 생성자 또한 메소드이므로 상속받아 수정, 변경해줄 수 있다. 그런데, 지금 상황을 보면 우리가 하고자하는 수정, 변경이라고 하기에는 이미 적었던 코드들을 다시 적고 거기서 수정을 해줘야하는 무척 불편한 형태이다. 이는 overriding이기 때문이다. 즉, 새롭게 정의한 것을 우선시하는 식의 로직이기 때문이다. 그렇다면 우리가 생각하는 수정,변경이 일어나기 위해서는 어떻게 해야할까? 

Super()

따라서 우리가 사용하는 것이 super이다. 결국 우리가 하고 싶은 것은 부모클래스를 부분적으로 수정하고 싶은 것이다. 이를 위해 사용되는 메소드가 super인 것이다. 사실 super()란 부모 클래스 객체를 가리키는 것이다. 즉, 여기서는 Robot이라는 부모 클래스 객체일 것이다. 따라서 __init__ 메소드를 수정하고 싶다면, __init__을 메소드 오버라이딩 해준 후에 부모 클래스의 __init__을 그대로 가져오는 과정이 필요할 것이고, 부모 클래스의 __init__super().__init__()을 통해서 가져올 수 있는 것이다. 즉, 이렇게 부분 상속이 가능해지는 것이다. 또한, 부모 클래스 객체의 init이 요구하는 인자가 있다면 그것을 추가적으로 넘겨줘야 한다.

class Siri(Robot):
    def __init__(self,name,age): 
    
        super().__init__(name)
        self.age = age
        
    def cal_mul(self,a,b):
        self.a = a 
        return a*b

    #### super의 활용 ####
    def cal_flexible(self,a,b):
        super().say_hi()
        self.say_hi()
        return self.cal_mul(a,b) + self.cal_add(a,b) +super().cal_add(a,b)
       
        
    def say_hi(self):
        # code..
        print(f"Greetings, my masters call me{self.name}.by apple.")

    
siri = Siri("iphone8",17)

siri.cal_flexible(4,7)
>>> Greetings, my masters call meiphone8.
>>> Greetings, my masters call meiphone8.by apple.
>>> 50

이 때 한가지 주의할 점은 super의 인자로 특정 클래스 객체를 넘겨줄 수 있는데, 이를 사용하는 이유는 상속이 여러 번 일어날 때 특정 부모클래스 객체를 지정하기 위해서 사용한다. 즉 위의 예시에서 super(Siri)라는 의미는 Siri의 직속 부모 클래스 객체인 Robot class를 의미하는 것이다.

추가적으로 이러한 상속을 통해 2가지 정도를 더 알아보도록 하겠다.

다중 상속

파이썬에서는 상속시 여러개의 부모클래스 객체를 한번에 상속받는 다중상속이 가능하다. 하지만, 이러한 다중상속은 Anti-Pattern으로 분류되는 경우가 많다. 그 이유는 상속받는 부모 클래스 객체끼리 동일한 name을 가진 메소드가 있다면 overriding될 것이고 이는 엄청 불편하고 유지보수를 어렵게 만들 것이다. 물론 단 하나의 product를 module화 시킬 때는 사용하는 경우도 많다고 한다.

class A:
    pass

class B:
    pass

class C:
    pass

class D(A,B,C):
    pass

print(D.mro())
>>> [<class '__main__.D'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>]
mro()
특정 객체의 상속관계를 보여주는 메소드로 앞에서부터 뒤로 자식-> 부모 순으로 클래스 객체를 보여준다.
파이썬에서 모든 것은 객체다.

그런데 위에서 우리가 상속한 적이 없는 object라는 클래스가 우리의 부모 클래스로 상속되고 있는 것을 볼 수 있다. 그 이유는 파이썬에서 모든 것은 객체이다라는 아주 중요한 문장과 이어진다. 지금까지 중요하다고 말했던 이 문장에 대해서 깊게 설명한 적이 없는데, 지금에서야 할 수 있게 되었다. 우선은 object 클래스 객체가 뭔지를 살펴보도록 하자.

사실 파이썬에서 모든 클래스들은 object라는 클래스 객체를 상속받는다. 그렇기 때문에 파이썬에서 모든 것이 객체인 것이다.( 이 object 클래스를 상속받아 만들어지기 때문에 ) 예를 들어보자. 우리는 3이라는 어떤 정수 인스턴스 객체를 종종 볼 수 있다. 이러한 정수 객체는 int라는 클래스를 통해서 찍혀진다. 이 int라는 클래스의 상속관계를 확인해보면 아래와 같이 object클래스가 있는 것을 볼 수 있다.

print(int.mro())
>>> [<class 'int'>, <class 'object'>]

결국 이렇게 파이썬에서는 이미 다 내장되어서 정의되어 있는 것들인 것이다. 이것이 바로 파이썬이라는 언어의 강력함이다. 내부적으로 많은 것들이 이루어지고 있기 때문에 다른 언어에 비해서 느리지만 아주 유용한 것이다. 

캡슐화 ( Encapsulation ) 

캡슐화는 객체의 속성과 행위를 하나로 묶고, 구현된 일부를 외부에 감춰 은닉한다. ( 외부로부터의 직접적인 접근을 차단한다.) 이 때 정보를 은닉한다는 것은 어떤 의미일까? 이를 위해서 우리는 Information Hiding 방식에 대해서 먼저 알아봐야 한다.

Public, Protect, Private

Class의 attribute, method에 대한 접근을 제어함으로써, 우리의 추상화된 클래스 정보를 어디까지 외부에 보일 것인지를 제어하는 방식이다. 일반적으로 Public으로 선언된 attribute와 method는 어떤 클래스라도 접근이 가능하다. 그리고 Protected로 선언된 attribute와 method는 해당 클래스 또는 해당클래스를 상속받은 클래스에서만 접근이 가능하다. 마지막으로 Private으로 선언된 attribute와 method는 해당 클래스 내에서만 접근이 가능하다. 

하지만, 파이썬에서는 모든 attribute와 method는 기본적으로 Public으로 클래스 외부에서 접근이 가능하다는 것이다.

class Robot: 
    """
    Robot class
    """
    
    def __init__(self,name,age):
        self.name = name
        self.age = age
        
       
    
ss = Robot("yss",8)
print(ss.age)
>>> 8

ss.age = -999
print(ss.age)
>>> -999

즉 이렇게 클래스 내의 attribute나 method에 접근이 가능하고, 변경까지 되는 것이다. 하지만, 이렇게 객체의 namespace가 전혀 보호되지 않고 있는 것은 위험한 것이다. 아주 작은 실수로 인해 전체에 문제가 생길 수 있는 것이다. 따라서, 외부에서 접근하지 못하게 할 필요가 있다. 

이 방법으로 사용하는 것이 ProtectPrivate이다. 그런데 파이썬에서는 사실 Protect라는 개념이 없다. 즉, 상속한 대상한테까지는 접근을 허용하되 외부에서는 접근을 못하게한다는 이러한 것이 파이썬에서는 없는 것이다. 그래서 protect는 파이썬 개발자들끼리한 일종의 약속으로 _를 앞에 하나만 붙여 protect로 선언한 것처럼 사용한다. 즉, 실제로 namespace가 보호되는 효과는 없지만, 이렇게 암묵적으로 암시적으로 약속을 하는 것이다. 즉, self._age = age와 같이 사용한다는 말이다.

class Robot: 
    """
    Robot class
    """
    
    def __init__(self,name,age):
        self.name = name
        self._age = age
        
       
    
ss = Robot("yss",8)
print(ss.age)
>>> 8

ss.age = -999
print(ss.age)
>>> -999

# 이렇게 접근은 가능하지만 위와같이 정의되었을 경우 접근하지 않는 것이 약속

물론 Private의 개념은 존재한다. 이는 protect와 유사하게 _를 두번 앞에 붙여 선언한다. 즉, __를 앞에 붙이게 되면 상속또한 되지 않는 것이다. (해당 클래스내에서만 볼 수 있기 때문에 ) 즉, 해당 객체의 namespace에서만 접근이 가능하고, 상속 혹은 외부참조등을 하려할 때는 해당 namespace에서 감춰지는 것이다. 

class Robot: 
    """
    Robot class
    """
  
    def __init__(self,name,age):
        self.name = name
        self.__age = age 
    
    def __say_hi(self):
        print( "hi")

    
ss = Robot("yss",8)
print(ss.age)
>>> AttributeError: 'Robot' object has no attribute 'age'

print(ss.say_hi())
>>> AttributeError: 'Robot' object has no attribute 'say_hi'

사실 파이썬에서 이러한 private기능을 사용하는데에는 재밌는 이유가 있는데 __를 name앞에 붙이게 되면 해당 name이 해당 클래스 객체의 name인 classname의 수정인 _classname__과 합쳐져 속성 혹은 메소드 이름으로 변경되기 때문이다. 이렇게 dir을 통해 확인해보면 class의 메소드가 된 것을 볼 수 있다. 

print(dir(Robot))
>>> ['_Robot__say_hi', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__']

이런 것을 파이썬에서는 name mangling이라고 한다. 따라서, 이렇게 아예 새로움 name으로 만들어버리기에 외부에서 접근할 메소드 자체가 없어진다. 하지만, 아래와 같이 _classname__뒤에 붙어 새로운 name으로 만들어진 것은 접근이 가능해진다. 이렇게 사실은 파이썬에서 private기능또한 없다는 것이다. 즉, 파이썬은 언어차원에서 캡슐화를 지원하지 않는 것이다. 캡슐화처럼 보이지만, 진짜 캡슐화가 되는 것은 아닌 것이다.

이렇게 객체의 속성과 행위를 하나로 묶고, 외부에 감추어 은닉하는 것이 파이썬에서의 캡슐화이다. 그런데, 파이썬에서는 좀 더 재미난 것들을 할 수 있다. 사실 생각해보면, 굳이 접근까지 못하게 해야하나라는 생각이 든다. 즉, read는 할 수 있어도 괜찮지 않을까? update나 write만 안되면 되는게 아닐까? 하는 생각이 드는데, 파이썬에서는 이에 맞게 재미난 기능들을 제공한다. 

getter & setter

getter와 setter는 각각 접근과 속성을 새롭게 setting할 수 있도록 만들어주는 역할을 한다. 즉, private으로 캡슐화된 어떤 attribute나 method에 대해서 getter와 setter를 통해서, 일부 캡슐화를 풀어줄 수 있다는 것이고, getter는 해당 속성에 접근이 가능하게 해주는 역할을 setter는 해당 속성을 바꿔줄 수 있도록 하는 역할을 한다는 것이다. 파이썬에서는 이러한 getter와 setter 개념을 사용하기 위해서 각각 getter는 @property 데코레이터를, setter는 @name.setter 데코레이터를 사용한다. 이 때 이러한 getter와 setter 설정을 위해 꾸며져야할 함수의 이름은 이러한 설정을 해주고자하는 변수명과 동일하게 작성하는 것이 관례다. 

class Robot: 
    """
    Robot class
    """
    
    def __init__(self,name,age):
        self.__name = name
        self.__age = age 
        
		
    @property
    def age(self):
        return self.__age
        
    @age.setter 
    def age(self,new_age):
        self.__age = new_age
        
    
    
droid = Robot("R2-D2",2)

print(droid.age)
>>> 2
droid.age=77
print(droid.age)
>>> 77

즉 새로운 함수를 만들어줘서 그 함수의 return이 private으로 설정했던 속성에 대한 접근, 변경이 가능하도록 하는 것이다. 따라서 해당 속성에 접근하기 위해서는 decorator가 적용되는 함수명을 통해 접근할 수 있게 된다. 그래서 위에서 age에 접근하는 것이다.

그러면 이러한 getter와 setter를 언제 사용하게 될까? 캡슐화된 상황을 가정해보자. 이 때 아예 접근이 불가능하지 않아야하며, 특정 조건을 만족했을 때만 변경이 가능한 조건부 캡슐화가 되어야한다. 이렇게 조건부 수정이 되어야하는 경우 사용하게 된다. 즉, 인스턴스의 변수 값에 대한 유효성 검사 및 수정시 사용되는 것이다.

class Robot: 
    """
    Robot class
    """
  
    def __init__(self,name,age):
        self.__name = name
        self.__age = age 

    @property
    def age(self):
        return self.__age
    
    @age.setter 
    def age(self,new_age):
        if new_age <0:
            raise TypeError('invalid range to age') 
        else:
            self.__age = new_age

  
droid = Robot("R2-D2",2)

droid.age += 1
print(droid.age)
>>> 3

droid.age += -999
print(droid.age)
>>> TypeError: invalid range to age

이렇게 우리의 직관에 부합하게 나이는 양의 방향으로만 변경되어야하기에 이러한 조건을 설정해두고, 그 조건에 맞지않는 수정을 하려할 때 error를 발생시켜주도록 조건에 맞춰 실행을 해주는 식의 코드를 작성할 수 있는 것이다.

이러한 캡슐화를 사용하는 것이 우리의 파이썬 프로그램을 아주 견고하게 만들어줄 것이다.

다형성( Polymorphism )


다형성은 객체를 부품화할 수 있도록 하는 것이다. 즉, 같은 형태의 코드가 다른 동작을 할 수 있도록 해준다는 의미이다. 예를 들어 어떤 A라는 공장에서 만든 타이어는 현대차의 타이어로도 기아차의 타이어도로 애플차의 타이어로도 동일하게 사용될 수 있다는 것이고, 이런 것을 다형성이라고 한다. 결국 코드의 반복을 줄이고, 여러 객체를 하나로 관리가 가능하게 만들어 유지보수적인 측면에서 좋은 것이다. 상속과도 이어지는 개념으로 하나의 부모 클래스를 상속받는 여러개의 자식클래스를 만들어 같은 코드를 다른 여러가지 형태로 만드는 것이다. 즉, 하나의 부모 클래스가 다른 것으로 해석이 될 수 있다는 것이다. 로봇이라는 클래스가 시리의 부품으로도, 빅스비의 부품으로도, 영어버전으로도, 한국어버전으로도 등등. 결국 같은 형태의 코드, 부품으로 구성되지만, 서로 다른 의미를 지닌 형태를 가지도록 한다는 것이 다형성을 가지고 있는 코드다. 그래서 결국 하나의 부품을 다양하게 사용할 수 있다는 의미로 해석하면 좋을 것 같다. 파이썬에서는 다형성의 개념이 약하기에 이정도로만 보고 넘어가도 괜찮을 것 같다.

class Robot: 
    """
    Robot class
    """
    
   # 복잡한 코드 ...
        
    
class Siri(Robot):
    def say_apple(self):
        print("hello my apple")
        
class SiriKo(Robot):
    def say_apple(self)":
        print("안녕하세요")
        
class Bixby(Robot):
    def say_samsung(self):
        print("hihi")

 

Reference 

https://jinmay.github.io/2019/11/23/python/python-class-first/

 

[파이썬]클래스의 getter 메소드와 setter 메소드(+ private 속성)

1234567class Student: def __init__(self, name, age): self._name = name self._age = agestu1 = Student('son', 20) 위와 같이 학생 클래스가 있을때 학생의 나이에 조건이 있어야 된다면 다음과 같은 코드가 필요할 것이다. 12

jinmay.github.io

https://velog.io/@kpl5672/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EA%B0%9D%EC%B2%B4%EC%A7%80%ED%96%A5-%EC%A0%80%EB%A6%AC

 

파이썬 객체지향(OOP) 정리

Chapter 1: 객체 지향 프로그래밍 시작하기Chapter 2: 객체를 만드는 법Chapter 3: 미리 알고가야 할 것들Chapter 4: 객체 만들기 연습Chapter 5: 객체 지향 프로그래밍 직접 해보기'속성'과 '행동'으로 이루어

velog.io

https://www.fun-coding.org/PL&OOP1-5.html

 

파이썬과 객체지향 프로그래밍: 객체지향 (public, private, protected) - 잔재미코딩

과제 1. 계좌 관리 class 작성하기 - attribute: 계좌 초기 금액을 속성으로 하나 설정 - 생성자에서 초기 금액은 0으로 설정 - 속성은 private 으로 설정 - method: 인출, 저축, 잔액 확인 세 가지 method 구현,

www.fun-coding.org

https://vincenthanna.tistory.com/entry/super-%EC%9D%98-argument

 

super() 의 argument

super() 에 type, obj argument를 사용하는 경우: class super(t: Any, obj: Any) super(type) -> unbound super object super(type, obj) -> bound super object; requires isinstance(obj, type) super(type, ty..

vincenthanna.tistory.com

윤상석 지식제공자 님의 타입 파이썬! 강의내용 : https://www.inflearn.com/course/%ED%83%80%EC%9E%85-%ED%8C%8C%EC%9D%B4%EC%8D%AC

 

타입 파이썬! 올바른 class 사용법과 객체지향 프로그래밍 - 인프런 | 강의

Python으로 생산성있는 개발만 아니라 견고하고 안전하게, 그리고 확장성있는 개발을 하세요! 🔥, - 강의 소개 | 인프런...

www.inflearn.com

 

'Python' 카테고리의 다른 글

Python - File Handling & With Statement  (0) 2022.02.16
Python - Module & Project  (0) 2022.02.15
Python - Magic Method  (0) 2022.02.13
Python OOP - Part 1 ( Object Oriented Programming)  (0) 2022.02.11
Python - Decorator  (0) 2022.02.02