---
# System prepended metadata

title: Object Oriented Programming

---

###### tags: Python
# Object Oriented Programming

creat a class like string
new_string = 'hello' --> instanciate
new_string is a object which orientes to the string class.

## 4 Pillars of OOP

### 1. Encapsulation - pack the method or attribute into class

### 2. Abstration - get access to method or attribute without knowing how it codes

### 3. Inheritance - to inherit the class
#### Example
```
class User: - Parent Class
    def __init__(self, email):
        self.email = email
        
    def sign_in(self):
        print("logged in")


class Wizard(User): -Subclass
    def __init__(self, name, power, email):
        super().__init__(email)
        self.name = name
        self.power = power
        
    def attack(self):
        print(f'attacking with power of {self.power}')
    
    
class Archer(User):
    def __init__(self, name, num_arrows):
        self.name = name
        self.num_arrows = num_arrows
        
    def attack(self):
        print(f'attacking with arrows: arrows left-{self.num_arrows}')

wizard1 = Wizard('Merlin, 50', merlin@gmail.com)
archer1 = Archer('Robin', 100)
wizard1.attack()
archer1.attack()
```
```
isinstance(instance, Class) - check the instance is involved in Class
isinstance(wizard1, Wizard)
```
#### Multiple inheritance - too complicated, rarely use

```
class HybridBorg(Wizard, Archer):
    def __init__(self, name, power, arrows):
        Archer.__init__(self, name, arrows)
        Wizard.__init__(self, name, power)

hb1 = HybridBorg('borgie', 50, 100)
```

### 4. Polymorphism - share the same method name with different output

#### Example 1

```
class PlayerCharacter 
(Upper for the first word)
    def __init__(self, name='anonymous', age=0):
            self._name = name -> attributes
            self._age = age

    def speak(self):
        print(f'my name is {self.name}, and I am {self.age} years old')
    
    @classmethod
    def adding_things(cls, num1, num2):
        return num1 + num2
player1 = PlayerCharacter('Tom', 20) - don't have to create a object to use method

    @staticmethod
    def adding_things(num1, num2):
        return num1 + num2 - without adding cls parameter into method
```
## Private and Public Variables
self._name = name --> to note that this is private variable not to overwrite

## Dunder Method - Built-in Method of object
`__init__` (double underscore called Dunder method)

```
calss Toy():
    def __init__(self, color, age):
        self.color = color
        self.age = age
```
        
    def __str__(self):
        return f'{self.color}'
        
```
action_figure = Toy('red', 0)
action_figure.__str__() == str(action_figure)
```

## MRO - Method Resolution Order(不要寫這種邏輯)

class A:
    num = 10

class B(A):
    pass
    
class C(A):
    num = 1

class D(B, C):
    pass
    
print(D.mro())
    
```
        A
     /     \
   /         \
  B            C
   \         /
     \     /
        D
```