SESAC LLM 개발자 강의/Python

[sesac LLM] day9-240112 Git, Class

잇연 2024. 1. 12. 16:32

Git

git 에서 branch를 만드는 명령어는?

git branch branch이름

 

Branch란?

  • 나무의 가지(branch)
  • 독립된 작업 공간
  • 지금 작업하던 내용을 그대로 유지한채로 새로운 개발이 가능
  • Branch한 스냅샷(commit)을 베이스(base)라고 부른다.
  • git branch -a 명령어로 현재 브랜치 확인 가능

git merge 자세히 보기

  • git merge 는 두 개의 브랜치를 하나로 합치는 과정입니다.
    • 병합하려는 브랜치로 이동합니다.
    • git checkout main
  • git checkout main
    • 병합할_브랜치 를 현재 브랜치에 합칩니다.
  • git merge 병합할_브랜치명

Fast-word merge

  • main 브랜치에 commit이 없다면, 가능한 시나리오
  • 현재 브랜치의 끝이 병합할_브랜치 의 시작점에 연결되는 경우, 단순히 포인터만 이동
  • 마치 브랜치가 한 줄기로 계속 이어진 것처럼 처리
1. 새로운 브랜치 fix 를 만들어 봐요.
	git branch fix
	git checkout fix
	git checkout -b fix : 위에 2 명령어를 한 번에 처리
2. ff.py 수정하기
1. print 함수에서 flush=True 넣어주기
3. 변경사항 commit
1. git commit -am "fix:ff.py"
4. 변경사항 main 브랜치에 반영하기
1. git checkout main
2. git merge fix
5

Recursive merge

  • 이후 설명들은 main 브랜치에 commit이 생겼다면 처리 할 수 있는 방법
  • 현재 브랜치와 main 브랜치의 공통 커밋을 하나 더 생성해서 합쳐버리는 방식
  • 병합할_브랜치 커밋들 유지됨.
  • git merge --no-ff 병합할_브랜치명
1. 새로운 브랜치 fix2 를 만들어 봐요.
	git checkout -b fix2
2. no-ff.py 수정하기
	print 함수에서 flush=True 넣어주기
3. 변경사항 commit
	git commit -am "fix:no-ff.py in fix2"
4. main 브랜치로 이동
	git checkout main
5. no-ff.py 또 수정해서 일부로 conflict 만들기
	1. 36번째 줄에 print문에 인수 모두 없애기
	2. git commit -am "fix:no-ff.py in main"
	3. git merge fix2
	4. conflict 구경하기 & 해결방법 생각하기
  1. Squash and merge
    • main 브랜치에 commit이 생겼다면 처리 할 수 있는 방법
    • 현재 브랜치와 main 브랜치의 공통 커밋을 하나 더 생성해서 합쳐버리는 방식
    • 병합할_브랜치 커밋들은 삭제됨.
    git checkout main
    git merge --squash 병합할_브랜치
    git commit -m 'message'
    
    1. 새로운 브랜치 fix3 를 만들어 봐요.
    	- git checkout -b fix3
    2. squash.py 수정하기
    	- print 함수에서 flush=True 넣어주기
    3. 변경사항 commit
    	- git commit -am "fix:squash.py in fix3"
    4. main 브랜치로 이동
    	- git checkout main
    5. squash.py 또 수정해서 일부로 conflict 만들기
    	36번째 줄에 print문에 인수 모두 없애기
    2. git commit -am "fix:squash.py in main"
    	 git merge fix3
    4. conflict 구경하기 & 해결방법 생각하기
    
    

Rebase and merge

main 브랜치에 commit이 생겼다면 처리 할 수 있는 방법 Base는 병합할_브랜치 가 분기된 브랜치 병합할_브랜치 의 Base를 최신 커밋으로 다시 설정. git checkout 병합할_브랜치 git rebase main git checkout main git merge 병합할_브랜치 10

새로운 브랜치 fix4 를 만들어 봐요.
git checkout -b fix4
2. rebase.py 수정하기
1. print 함수에서 flush=True 넣어주기
3. 변경사항 commit
1. git commit -am "fix:rebase.py in fix4"
4. main 브랜치로 이동
1. git checkout main
5. rebase.py 또 수정해서 일부로 conflict 만들기
1. 36번째 줄에 print문에 인수 모두 없애기
2. git commit -am "fix:rebase.py in main"
3. git merge fix4
4. conflict 구경하기 & 해결방법 생각하기
  1. 병합 후 브랜치 정리 병합이 완료된 후에는 더 이상 필요하지 않은 브랜치를 정리합니다. 예시 명령어: git branch -d 새브랜치명 git push origin --delete 새브랜치명 참고: Git의 다양한 브랜치 병합 방법 (Merge, Squash & Merge, Rebase & Merge) 12

 


클래스

클래스란 무엇인가?

  • 클래스는 객체를 생성하기 위한 일종의 템플릿
  • 객체의 속성(attributes)과 행동(methods)을 정의하며, 데이터와 함수를 하나의 캡슐화 된 단위로 묶는다.
  • 예를 들어 '자동차' 클래스는 속성(색상, 브랜드)과 메소드(운전, 정지)를 가질 수 있다.

객체 지향 프로그래밍(OOP)과 클래스

  • 객체 지향 프로그래밍은 프로그램을 객체들의 집합으로 파악하는 프로그래밍 패러다임.
  • 절차 지향 프로그래밍의 대안으로 나왔다.
  • 클래스는 이러한 OOP의 핵심 요소로, 실세계의 사물을 모델링하고 프로그램 내에서 그 행동을 정의한다.
  • OOP의 주요 특징은 상속, 다형성, 캡슐화 같은 것들이 있는데, 핵심은 클래스와 객체이다.

예제 코드:

class Car:
    def __init__(self, color, brand):
        self.color = color
        self.brand = brand
    
    def drive(self):
        return f"{self.brand} 붕붕"
    def stop(self):
        return f"{self.brand} 끼이익"

my_car = Car("회색", "모닝")
print(my_car.drive())
print(my_car.stop())

클래스 선언 및 기본 구조

  • 클래스는 class 키워드로 만든다.
  • 클래스의 이름은 일반적으로 파스칼 이스(PascalCase)를 사용한다.
  • 클래스 내부에는 데이터를 나타내는 속성(attributes)과 기능을 나타내는 메소드 (methods)가 정의된다.
class Wizard:
	def __init__(self, name, age, skill):
		self.name = name
		self.age = age
		self.skill = skill
	def greet(self): # 메소드
		return f"안녕? 나는 {name}이야. 올해 {age}살 됐어. 내 {skill}맛 좀 볼래?"
# 객체 생성
dumbledore = Wizard("덤블도어", 60, '엑스펙토 파트로눔')
print(dumbledore.greet())

Wizard 클래스는 name 과 age , skill 3개의 속성을 가지며, greet 메소드를 통해 인 사를 한다. 우리가 게임에서 보는 NPC들은 모두 이렇게 만들어 졌을 것이다.

객체(Objects):

  • 객체는 클래스에 의해 생성된 데이터 구조. 클래스에 정의된 속성과 행동을 가진다.
  • 객체는 실세계의 사물이나 개념을 소프트웨어 내에서 표현한 것
  • 파이썬에서 모든 것은 객체로, 숫자, 문자열, 리스트, 함수 등도 객체이다.
  • 예를 들어, Car 클래스를 바탕으로 만들어진 각각의 차량은 Car 객체일 것이다.

인스턴스(Instances):

인스턴스는 클래스의 정의에 따라 메모리에 생성된 객체를 의미한다. 특정 클래스의 구조와 기능을 가진 객체를 그 클래스의 인스턴스라고 합니다. 사람의 설계도 같은 것이 있다고 할 때, 그 설계도가 클래스이며, 우리가 개와 사람을 구분할 수 있듯이 객체는 그 클래스가 세상에 드러난 것으로 볼 수 있다. 사람들 마다 나이도 다르고 생김새도 다르듯이 각 객체의 고유성이 강조되면 인스턴스라 볼 수 있다. 큰 차이 없이 써도 사실상 무방할 것 같다. 

class Panda:
	def __init__(self, name):
		self.name = name
# Panda 클래스의 인스턴스 생성
pubao = Panda("푸바오")
print(isinstance(pubao, Panda)) # True
9

클래스와 인스턴스의 차이는?

A) 클래스는 메모리에 생성된 객체이며, 인스턴스는 클래스의 정의를 나타낸다.
**B) 클래스는 객체의 설계도와 같고, 인스턴스는 그 설계도에 따라 생성된 실제 객체이다.**
C) 클래스와 인스턴스는 같은 개념이며, 차이가 없다.
퀴즈 2
2. 객체 지향 프로그래밍의 핵심 특징?
A) 객체 지향 프로그래밍은 함수의 집합으로 프로그램을 구성한다.
B) 객체 지향 프로그래밍에서 클래스는 불필요하며, 모든 코드는 절차적으로 실행된다.
**C) 객체 지향 프로그래밍은 실세계의 사물을 모델링하여 프로그램 내에서 객체의 행동**

속성과 메소드

  • 속성(Attribute): 클래스에 속한 데이터. 인스턴스의 상태를 저장한다.
  • 메소드(Method): 클래스에 속한 함. 해당 클래스의 객체가 수행할 수 있는 행동을 정의.
  • 속성은 init 메소드 내에서 정의되며, 'self' 키워드를 통해 클래스의 다른 메소드에서 접근할 수 있음.
  • 메소드는 객체의 행동을 정의하며, 첫 번째 파라미터로 self 를 받아 인스턴스에 접근한다. 주의사항
    • 이름이 self인 것은 문법이 아니라 관습(convention)!
    • 클래스 생성 시 init 안해도 될까?
    • 속성을 할당할 필요가 없으면 안해도 된다.
  • 예제코드:
# Panda 클래스 정의
class Panda:
    def __init__(self, name, breed):
        self.name = name  # 이름 속성
        self.breed = breed  # 품종 속성

    def eat_bamboo(self):  # 대나무 먹는 메소드
        return f"{self.name}가 또 대나무를 먹습니다."

# 객체 생성
my_dog = Panda("푸바오", "자이언트 판다")
print(my_dog.eat_bamboo())

Panda 클래스는 판다의 이름과 품종을 속성으로 가지며, eat_bamboo 메소드로 대나무 먹 는 행동을 나타낸다.

이닛메소드:

init 메소드: 생성자

  • init 메소드는 클래스를 생성하는, 즉 초기화하는 역할을 하는 메소드이다.
  • 객체의 초기 상태를 정의해 줘야 객체로서 의미를 갖는다.
  • 예: 게임에서 처음 생성된 캐릭터. 레벨 1의 경험치 0
  • 예: 갓 태어난 아기 객체 외부와의 상호작용으로 객체의 상태가 변하면서 점차 객체들 간의 차이가 발생한다.

클래스 인스턴스 생성

  • 클래스 인스턴스는 클래스명(인자...) 형태로 생성.
  • 생성 시 init 메소드에 정의된 매개변수에 해당하는 인자를 전달해줘야 한다.
  • 예제 코드:
  • new_laptop = Laptop("Apple", "MacBook Pro") print(new_laptop.describe())

여기서 new_laptop 은 Laptop 클래스의 새 인스턴스이다.

메소드 호출과 속성 접근

  • 메소드 호출은 인스턴스명.메소드명(인자1, 인자2, ...) 형태로 이루어집니다.
  • 속성에 접근할 때는 인스턴스명.속성명을 사용합니다.
  • 예제 코드를 통해 이를 설명합니다:
pythonCopy code
# 메소드 호출과 속성 접근 예제
print(my_laptop.brand)  # 'Apple'
print(my_laptop.model)  # 'Macbook Pro'
print(my_laptop.describe())  # '2020년형 인텔 어쩌구저쩌구'

위 코드에서 my_laptop 인스턴스를 통해 브랜드와 모델 속성에 접근하고, describe 메소드를 호출한 예제입니다.

  • **my_laptop.brand**는 my_laptop 인스턴스의 브랜드 속성에 접근합니다.
  • **my_laptop.model**은 my_laptop 인스턴스의 모델 속성에 접근합니다.
  • **my_laptop.describe()**는 my_laptop 인스턴스의 describe 메소드를 호출합니다.

이처럼, 인스턴스를 통해 속성과 메소드에 접근하여 해당 속성의 값을 가져오거나 메소드를 실행할 수 있습니다.


퀴즈

class Dog:
    def __init__(self, name, breed):
        self.name = name
        self.breed = breed
    
    def bark(self):
        return "멍멍!"

my_dog = Dog("마루", "시바견")
  • A) 클래스 이름 옆에 () 표시를 빠트렸다.
  • B) __init__ 메소드의 매개변수가 잘못됐다.
  • C) bark 메소드 정의가 잘못됐다.
  • D) 인스턴스 생성이 잘못됐다.
  • E) 이 코드는 잘못되지 않았다.

클래스변수란?

  1. 클래스 변수는 클래스 정의 내에서 선언되며, 모든 인스턴스가 공유하는 변수입니다.
  2. 이것은 생성자의 인스턴스 변수와는 구별되어야 합니다. 인스턴스 변수는 각 인스턴스가 고유한 값을 가지지만, 클래스 변수는 모든 인스턴스가 동일한 값을 공유합니다.
  3. 클래스 변수는 클래스 자체에 속하며, 클래스의 이름을 통해 접근할 수 있습니다. 클래스명.클래스변수 형식으로 접근하고, 클래스명.클래스변수 = 새로운값 형식으로 값을 할당할 수 있습니다.
  4. 한 인스턴스가 클래스 변수의 값을 변경하면 다른 인스턴스의 클래스 변수도 그 변경된 값으로 변경됩니다. 이는 모든 인스턴스가 클래스 변수를 공유하기 때문입니다.
  5. 클래스 변수는 전역 변수를 클래스 내에서 사용하는 것과 유사한 개념이며, 데이터를 클래스 수준에서 관리하고 공유할 때 유용합니다.

예시로 스타크래프트의 질럿에서 언급한 경우, "속도"라는 클래스 변수는 모든 질럿 인스턴스가 공유하며, 한 인스턴스에서 속도를 변경하면 모든 질럿의 속도가 변경될 것이고, "HP"는 각 질럿 인스턴스마다 고유한 값을 가지게 됩니다.

예제

# 클래스 정의
class Zealot:
    velocity = 1.00  # 클래스 변수
    
    # 생성자 메소드
    def __init__(self, hp, shield, damage):
        self.hp = hp  # 인스턴스 변수
        self.shield = shield  # 인스턴스 변수
        self.damage = damage  # 인스턴스 변수
    
    # 메소드
    def roar(self):
        print("My life for Aiur!")

# 인스턴스 생성
zealot_1 = Zealot(100, 60, 8)
zealot_2 = Zealot(100, 60, 8)

# 클래스 변수와 인스턴스 변수 사용
print(Zealot.velocity)  # 클래스 변수 출력: 1.00
print(zealot_1.velocity)  # 인스턴스에서 클래스 변수 출력: 1.00
print(zealot_2.velocity)  # 인스턴스에서 클래스 변수 출력: 1.00

# 클래스 변수 값 업데이트
Zealot.velocity = 1.05

# 업데이트된 클래스 변수 값 출력
print(Zealot.velocity)  # 클래스 변수 출력: 1.05
print(zealot_1.velocity)  # 인스턴스에서 클래스 변수 출력: 1.05
print(zealot_2.velocity)  # 인스턴스에서 클래스 변수 출력: 1.05

만일, zealot_1만 속도가 빨라지는 버프를 받았다. 그럼 모든 Zealot이 빨라질까? zealot_1.velocity = 1.1 print(Zealot.velocity) # 값은? 19

class Zealot:
    velocity = 1.00  # 클래스 변수
    
    def __init__(self, hp, shield, damage):
        self.hp = hp  # 인스턴스 변수
        self.shield = shield  # 인스턴스 변수
        self.damage = damage  # 인스턴스 변수

# 두 개의 Zealot 인스턴스 생성
zealot_1 = Zealot(100, 60, 8)
zealot_2 = Zealot(100, 60, 8)

# 클래스 변수 velocity 변경
Zealot.velocity = 1.10  # 올바른 방법

# 결과 확인
print(Zealot.velocity)  # 1.10
print(zealot_1.velocity)  # 1.10
print(zealot_2.velocity)  # 1.10

상속의 개념과 장점

  • 상속(Inheritance)은 한 클래스(자식 클래스)가 다른 클래스(부모 클래스)의 멤버(속성과 메소드)를 물려받는 것을 의미한다.
  • 기존 클래스의 재사용을 목적으로 한다.
  • 자식 클래스는 부모 클래스의 모든 기능을 사용할 수 있으며,
  • 필요에 따라 새로운 기능을 추가하거나 기존 기능을 수정(오버라이딩)할 수 있다.

파이썬에서 상속 구현하기

파이썬에서 상속은 부모 클래스를 자식 클래스 정의에 괄호 안에 명시함으로써 구현된다. super() 함수를 사용하면, 부모 클래스의 메소드에 접근할 수도 있다. 예제 코드:

class Unit:
    def __init__(self, name, health):
        self.name = name
        self.health = health

    def move(self):
        return f"{self.name} 이동 중"

class Marine(Unit):
    def __init__(self, name, health, damage):
        super().__init__(name, health)
class Unit:
    def __init__(self, name, health):
        self.name = name
        self.health = health

    def move(self):
        return f"{self.name} 이동 중"

class Marine(Unit):
    def __init__(self, name, health, damage):
        super().__init__(name, health)
        self.damage = damage

    def attack(self):
        return f"{self.name} 공격! 데미지: {self.damage}"

class Medic(Unit):
    def heal(self, target):
        return f"{self.name}이(가) {target.name} 치료 중"

class Firebat(Unit):
    def __init__(self, name, health, damage):
        super().__init__(name, health)
        self.damage = damage

    def attack(self):
        return f"{self.name} 화염 방사! 데미지: {self.damage}"

# 인스턴스 생성
marine = Marine("마린", 100, 20)
medic = Medic("메딕", 60)
firebat = Firebat("파이어뱃", 120, 30)

# 메소드 호출
print(marine.move())          # '마린 이동 중'
print(marine.attack())        # '마린 공격! 데미지: 20'
print(medic.heal(marine))     # '메딕이(가) 마린 치료 중'
print(firebat.attack())       # '파이어뱃 화염 방사! 데미지: 30'
  • Unit 클래스는 모든 유닛의 공통적인 기능(이동)과 속성(이름, 체력)을 정의합니다.
  • Marine, Medic, Firebat 클래스는 Unit 클래스를 상속받아 각각 고유한 기능(공격, 치 료, 화염 방사)을 추가합니다.
  • super().init(name, health)를 통해 부모 클래스인 Unit의 생성자를 호출하여 공통 속성 을 초기화합니다. 25

퀴즈

class Unit:
    def __init__(self, name, health):
        self.name = name
        self.health = health

    def move(self):
        return f"{self.name} 이동 중"

class Medic(Unit):
    def heal(self, target):
        return f"{self.name}이(가) {target.name} 치료 중"

A) heal 메소드
B) move 메소드
C) attack 메소드
D) __init__ 메소드

다형성: 메소드 오버라이딩과 오버로딩

  • 다형성은 같은 이름의 메소드가 다른 클래스 또는 같은 클래스 내에서 다른 행동을 할 수 있 게 하는 프로그래밍 개념이다.

메소드 오버라이딩(Method Overriding):

상속 관계에 있는 클래스에서 부모 클래스의 메소드를 자식 클래스에서 재정의하는 것이 다.

  • 상속받는 클래스에 따라 다른 기능을 수행할 수 있다.

메소드 오버로딩(Method Overloading):

  • 같은 이름의 메소드가 매개변수의 유형이나 개수에 따라 다른 행동을 하는 것입니다. 파이썬은 오버로딩을 직접적으로 지원하지 않지만, 비슷한 효과가 있는 방법이 있다고 알려져 있다. 이 수업에서는 다루지 않겠다.