Python Syntax 2 for A.I.

nalimeitb·2025년 6월 23일

String

  • 문자열 자료형
    - sequnce 자료형으로 문자형 data를 메모리에 저장.
    - 영문자 한 글자는 1byte의 메모리 공간을 사용함.
    - string은 1byte 크기로 메모리 공간이 할당된다.

  • 1byte 메모리 공간
    이진수 한 자릿수는 1bit로 저장되는데,
    1byte = 8bit = 2^8 즉 256까지 저장이 가능하다.
  • 컴퓨터는 문자를 직접적으로 인식하지 못하며, 모든 데이터를 2진수로 인식함.
  • 이를 위해 2진수를 문자로 변환하는 표준 규칙을 정함. ex) ASCII, UTF-8, CP-949 등
    - 이러한 규칙에 따라 문자를 2진수로 변환하여 저장하거나, 저장된 2진수를 숫자로 변환하여 표시함.
    - 예를 들어) UTF-8 기준으로 대문자 U는 이진수로 "1000011"로 변환됨.


-> 1byte 로 표현할 수 있는 범위(ASCII Code)

import sys
print(sys.getsizeof("a"), sys.getsizeof("ab"), sys.getsizeof("abc"))
#50 51 52

-> sys 모듈을 import해서 getsizeof 메서드를 이용해, 각 메모리 사이즈를 출력해볼 수 있다.

-> 각 데이터 타입 별로 할당 받을 수 있는 공간의 크기가 다르다.

  • 메모리 공간에 따라 표현할 수 있는 숫자의 범위가 다르다.
    - ex)
    4byte = 32bit = 2^32
    -2.147M ~ +2.147M까지 표시(2^32이지만 앞에 부호를 제외하고 2^31)
  • 데이터 타입은 메모리의 효율적 활용을 위해 매우 중요하다.

Indexing

  • 문자열의 각 문자는 개별 주소(offset)을 가진다.
  • 이 주소를 사용해 할당된 값을 가져오는 것을 indexing이라고 한다.
  • List와 같은 형태로 data를 처리한다.

a = 'abcde'
print(a[0], a[4])
#a e
print(a[-1], a[-5])
#e a
>>> a = "Artificial Intelligence and Machine Learning"
>>> print (a[0:6], " AND ", a[-9:]) # a 변수의 0부터 5까지, -9부터 끝까지
Artifi AND Learning
>>> print (a[:]) # a변수의 처음부터 끝까지
Artificial Intelligence and Machine Learning
>>> print (a[-50:50]) # 범위를 넘어갈 경우 자동으로 최대 범위를 지정
Artificial Intelligence and Machine Learning
>>> print (a[::2], " AND ", a[::-1]) # 2칸 단위로, 역으로 슬라이싱
Atfca nelgneadMcieLann AND gninraeL enihcaM dna ecnegilletnI laicifitrA

-> 문자열의 offset 값을 기반으로 문자열의 부분값을 반환할 수 있다.
-> [a:b:c] : a부터 b-1까지 c 간격으로

string operation

  • 덧셈과 뺄셈 연산이 가능하다.
  • in 연산자로 포함 여부를 체크할 수 있다.
>>> a = “TEAM"
>>> b = “LAB"
>>> print (a + " " + b) # 덧셈으로 a와 b 변수 연결하기
TEAM LAB
>>> print (a * 2 + " " + b * 2)
TEAMTEAM LABLAB # 곱하기로 반복 연산 가능
>>> if ‘A' in a: # ‘U‘가 a에 포함되었는지 확인
... print (a)
... else:
... print (b)
...
TEAM

string method


다양한 문자열 표현

1) 문자열 선언 시, 큰따옴표나 작은 따옴표를 활용하는데 이 때, 내가 이러한 문자들을 출력하고 싶으면 어떻게 해야할까?

  • 백슬래쉬 활용 : 백슬래쉬 + ' 는 문자열 구분자가 아닌 '를 그대로 출력해줌
    ex) a = 'It\'s Ok.' #It's Ok.

  • 큰따옴표로 묶기 : 큰따옴표로 문자열 선언 후, 작은따옴표를 출력 문자로 사용.
    ex) a = "It's Ok." #It's Ok.

2) 두 줄 이상의 문자열 표현은 어떻게 할까?

  • 큰따옴표 또는 작은 따옴표 연속 세번 사용하기.
    ex)
    a = """ It's Ok.
    I'm Happy.
    See you. """
  • \n : \n은 줄바꿈을 의미하는 특수문자(이스케이프 문자열임.)

    -> 특수문자 예시

raw string

  • 특수문자 특수 기호인 \ escape 글자를 무시하고 그대로 출력해버린다.
raw_string = "난 네부캠 ai-tech 8기에 합격한다. \n 레알"
print(raw_string)
#난 네부캠 ai-tech 8기에 합격한다.
# 레알

raw_string = (r"난 네부캠 ai-tech 8기에 합격한다. \n 레알")
print(raw_string)
#난 네부캠 ai-tech 8기에 합격한다. \n 레알

Advanced Function Concept

함수에서 parameter를 전달하는 방식

  • 값에 의한 호출 (Call by Value)
    - 함수에서 인자를 넘길 때, 값만 넘김.
    • 함수 내에 인자 값을 변경해도 호출자에게 영향을 주지 않음.
  • 참조에 의한 호출 (Call by Reference)
    - 함수에 인자를 넘길 때, 메모리 주소를 넘김.
    • 함수 내에 인자 값을 변경하면, 호출자의 값도 변경됨.
    • 마치 C언어의 pointer 개념과 비슷.
  • 객체 참조에 의한 호출 (Call by Object Reference)
    - 파이썬은 객체 참조에 의한 호출 방식을 따름.
    - 파이썬은 "객체의 주소"가 함수로 전달된다.
    - 전달된 객체를 참조해서 변경하면 호출자에게 영향을 준다.
    하지만 새로운 객체를 만들 경우, 호출자에게 영향을 주지 않는다.

call by object reference

def spam(eggs) :
	eggs.append(1)
    eggs = [2, 3]
    
ham = [0]
spam(ham)
print(ham) #[0, 1]

흐름을 따라가 보자!
-> 기존 객체의 주소값에 [1]을 추가함 - eggs.append(1)
-> 새로운 객체 생성 - eggs = [2,3]

  • ham과 eggs는 같은 주소를 가리켰으나,
    eggs에 [2,3]을 할당하면서 같은 곳을 가리켰던 화살표가 끊어졌다.

swap

  • 함수를 통해 변수 간의 값을 교환(Swap)하는 함수
  • Call By XXXX를 설명하기 위한 전통적인 함수 예시
def swap_value (x, y):
	temp = x
	x = y
	y = temp

def swap_offset (offset_x, offset_y):
	temp = a[offset_x]
	a[offset_x] = a[offset_y]
	a[offset_y] = temp

def swap_reference (list, offset_x, offset_y):
temp = list[offset_x]
list[offset_x] = list[offset_y]
list[offset_y] = temp
a = [1, 2, 3, 4, 5]

#1
swap_value(a[0], a[1])
print(a)
#[1, 2, 3, 4, 5]

#2
swap_offset(0, 1)
print(a)
#[2, 1, 3, 4, 5]

#3
swap_reference(a, 3, 4)
print(a)
#[1, 2, 3, 5, 4]

#1.
x에 a[0] 값이 들어가고, y에 a[1] 값이 들어간다.
temp는 x, 즉 a[0]를 가리키게 되는데,
이때 값은 각각, 1와 2을 가리키고 메모리는 고정되어 있다.(-5~256)
그 다음 줄 코드에서, x는 a[1]를 가리키게 된다.
그 다음 줄 코드에서, y 는 temp를 가리킨다.
temp는 1을 가리키기 때문에 a[0]을 가리키게 된다.
리스트 자체가 들어가서 바뀌지 않고 값을,
변수가 가리키는 주소값만 계속 바뀌게 되는 것이다.
그렇기 때문에, 리스트 자체에는 전혀 영향을 주지 않는것이다.

#2.
offset만 주어진다.(함수 밖에 있는 리스트를 불러와서 쓴다.)
temp의 값에 리스트 a의 0번째 인덱스가 저장된다.
그 다음 코드에서 할당을 새롭게 해주는데,
리스트 자체를 다루기 때문에 a의 0번 인덱스 값을,
a의 1번 인덱스 값으로 새롭게 할당한다.
그리고 a의 1번 인덱스 값을 temp에 들어있던
값으로 새롭게 할당해준다.

#3.
세번째는 아예 reference list도 같이 넣어주는 것이다.
리스트 값이 함수에 들어가기 때문에,
이 리스트는 객체가 유지가 된다.

temp에 4가 저장되고,
그 다음 코드에서 a[3]은 5에 할당되고,
그 다음 코드에서 a[4]는 4가 들어가게 되면서
리스트에 변화가 생긴다.

  • 파이썬 함수 내에서 객체내에서 함수의 메모리주소가 어떻게 유지가 되는지를 추적하라!

  • 함수 내에서 뭔가를 쓸 때, 새로 들어온 값을 복사를 하는 것이 좋다!
    예를 들면, 3번 예시에서라면 temp_list = a[:] 이런식으로.

  • 되도록이면, 들어온 객체를 직접 만지지 않는 것이 좋다!
    특히 append 같은거 값자체가 바뀌어버릴 수 있다.

Fuction-Scoping Rule

Scoping Rule 이란?

  • 변수가 사용되는 범위(함수 또는 메인 프로그램)
  • 지역변수(local variable) : 함수 내에서만 사용
    전역변수(global variable) : 프로그램 전체에서 사용
def test(t):
	print(x)
	t = 20 #Local Variable
	print ("In Function :", t)
    
x = 10 #Global Variable
test(x)
print(t)
  • x에 10을 넣으면, test라는 함수에 x에 10이 들어간다.
    x는 함수 안에서, t라는 이름으로 바뀐다.
    print(x)를 하면 이 10이 출력이 된다.
    t는 20이라고 하면 In Function : 20이 출력이 된다.
    그리고 마지막 print(t)를 하면,
    t는 없는 변수이기 때문에(함수 내 지역변수이기 때문에)
    NameError가 발생한다.
def test(t):
	t = 20
	print ("In Function :", t)

x = 10
print ("Before :", x) # 10
test(x) # 함수 호출 # In Function : 20
print ("After :", x) # 10
  • 함수를 호출하면, t = 20으로 새로운 값을 할당받게 되고,
    x 의 값을 t가 가리키는 관계가 끊어져 버린다.
    In Function 뒤에 나오는 값은 20이 된다.
def f():
	s = "I love London!"
	print(s)

s = "I love Paris!"
f()
print(s)
  • 함수 안에서 사용되던, 지역변수의 값은
    밖에서도(전역변수로도) 사용될 수 있다.
    하지만 똑같은 변수 s 이지만, 같은 값은 아니다!
    이를테면, 함수 안에서는 f.s 인 것이고,
    함수 밖에서는 전역변수 G.s 인 것이다.
    그렇기 때문에 마지막 줄 코드 print(s)를
    실행시켜보면, I love Paris!를 출력한다.
def f():
	global s
    s = "I love London!"
	print(s)

s = "I love Paris!"
f()
print(s)
  • 만약 이것을 같은 변수로 선언하고 싶다면,
    전역변수를 의미하는 global 키워드를 사용하여
    함수 안에서 global s 를 선언해주면 된다.
  • print(s) 실행 시, I love London! 출력된다.

Recursive Function

재귀함수 Recursive Function 이란?

  • 자기 자신을 호출하는 함수
  • 점화식과 같이 재귀적 수학 모형을 표현할 때, 사용
  • 재귀 종료 조건이 존재하고 이 조건을 만족할 때 까지 함수 호출을 반복
def factorial(n):
	if n == 1:
		return 1
	else:
		return n * factorial(n-1)

print (factorial(int(input("Input Number for Factorial Calculation: "))))
  • n의 값이 1이 될 때 까지, factorial()을 실행한다.
  • 이렇게 종료조건을 만족할 때 까지, 함수를 실행하는 코드를 재귀함수라고 한다.

위 코드를 for loop로 변경한다면?

def factorial(n) :
	result = 1
	if n == 1 :
    	return result
    else :
    	for i in range(n, 1, -1) :
        	result = result * i
        return result

Function Type Hints

  • 파이썬의 특징 중 하나는 바로, Dynamic Typing
  • 하지만, 이 방식이 문제가 될 수 있는데 처음 함수를 사용하는 사용자가
    interface(type)을 알기 어렵다는 단점이 있다.
  • python 3.5 version 이후로는, PEP 484에 기반하여 type hints 기능을 제공할 수 있다.
    이로 인해, 다른 사용자가 해당 함수를 조금 더 쉽게 사용할 수 있다는 장점이 있다.
def do_fuction(var_name : var_type) -> return_type :
	pass
->
example)
def type_hint_example(name : str) -> str :
	return f"Hello, {name}"
  • Type Hints의 장점
    - 사용자에게 interface를 명확하게 알려줄 수 있다.
    • 함수의 문서화 시, parameter에 대한 정보를 명확히 알 수 있다.
    • mypy 또는 IDE, linter 등을 통해 코드의 발생 가능한 오류를 사전에 확인할 수 있다.(?!)
    • 시스템 전체적인 안정성을 확보할 수 있다.
#example pytorch

def insert(self, index : int, module : Module) -> None :
	r""" Insert a given module before a given index in the list.
	Arguments :
    	index (int) : index to insert.
        module (nn.Module) : module to insert
    """
  • insert 함수의 파라미터 중, index는 정수형이구나! 반환값은 None 이구나! 알 수 있게 된다.

docstring

  • docstring 이란? 함수의 상세한 내용에 대해서 사용자의 이해도를 높이기 위해 작성하는 문구를 말한다.
  • 파이썬 함수에 대한 상세스펙을 사전에 작성 -> 함수 사용자의 이행도 UP
  • 세 개의 따옴표로 docstring 영역 표시(함수명 아래에 위치)

  • 작성 방식 :
    - 먼저, 함수의 목적을 작성
    • 그 아래 parameter들에 대한 설명
    • 그 다음, return 값에 대한 설명
    • (선택) example
  • 직접 작성해도 좋지만, vs code의 extension을 이용하면 양식을 자동으로 생성해준다.(vs code extension docstring generator)

!function guideline*

함수 작성 가이드 라인

  • 함수는 가능하면 짧게 작성할 것(최소 단위, 짧게 여러개).

  • 함수 이름에 함수의 역할, 의도가 명확히 드러낼 것.
    함수의 이름은 VO 형태로 많이 생성된다. V는 verb, O는 object

  • 하나의 함수에는 유사한 역할을 하는 코드만 포함할 것.

    #적절
    def add_variables(x,y) :
        return x + y
    
    #부적절함
    def add_variables(x,y) :
        print(x, y)
        return x + y
  • 인자로 받은 값 자체를 바꾸지 말 것
    (임시변수로 선언하거나 또는 복사해서 사용할 것)

    #이것 보다는
    def count_word(string_variable) :
        string_variable = list(string_variable)
        return len(string_variable)
    
    # 이게 낫다는 거지
    def count_word(string_variable) :
        return len(string_variable)
  • 공통적으로 사용되는 코드는 함수로 변환

  • 복잡한 수식, 조건은 식별 가능한 이름의 함수로 변환할 것.

#이것 보다는,
import math
a = 1; b = -2; c = 1
print ((-b + math.sqrt(b ** 2 - (4 * a * c)) ) / (2 * a))
print ((-b - math.sqrt(b ** 2 - (4 * a * c)) ) / (2 * a))

#이게 낫다는 거지
import math
def get_result_quadratic_equation(a, b, c):
values = []
values.append((-b + math.sqrt(b ** 2 - (4 * a * c)) ) / (2 * a))
values.append((-b - math.sqrt(b ** 2 - (4 * a * c)) ) / (2 * a))
return values
print (get_result_quadratic_equation(1,-2,1))

How to write Good Code

  • 사람이 이해하기 좋은 코드가 바로 좋은 코드다!
  • 사람이 이해하기 좋은 코드는 어떻게?
    규칙이 필요하다 -> 이 규칙을 Coding Convention이라고 한다.
    • 중요한 것은 일관성, 읽기 좋은 코드가 좋은 코드이다.
    • 들여쓰기는 4 space를 권장한다(tab이든 space든 혼합하지만 말아라!)
    • 한 줄은 최대 79자 까지!
    • 불필요한 공백은 피하라!
    • 연산자는 1칸 이상 띄우지 말라!
    • 주석은 항상, 갱신하고 불필요한 주석은 삭제하라!
    • 코드의 마지막에는 항상 한 줄 추가하라!
    • 소문자 l, 대문자 O, 대문자 I 금지
    • 함수명은 소문자로 구성하고, 필요하다면 밑줄로 나누어라! 등
  • 이러한 규칙들을 "flake8" module로 체크할 수 있다.
  • 최근에는 black module을 활용하여 pep8 like 수준을 준수 할 수 있다.
    black codename.py 명령을 사용한다. (실행 시, 자동으로 어느정도 수정해준다.)

0개의 댓글