변수(Variable)란 가장 기초적인 프로그래밍 문법 개념으로, 데이터(값)을 저장하기 위한 메모리 공간의 프로그래밍상 이름이다. 변수는 메모리 주소 를 가지고 있고 변수에 들어가는 값 은 메모리 주소에 할당된다. 그래서 변수가 선언되는 순간 메모리 특정영역에 물리적인 공간이 할당되며 해당 값은 메모리에 저장된다.
데이터가 100개, 혹은 1000개 이상 존재한다면 어떻게 관리할까? 일일이 변수를 선언하는 것은 비효율적일 것이다. 그래서 파이썬에서는 리스트를 활용하여 손 쉽게 데이터를 관리할 수 있다.
리스트는 시퀀스 자료형으로 int,float와 같은 다양한 데이터 타입을 하나의 리스트에 포함할 수 있다.
리스트의 특징은 크게 다음과 같다.
colors=['red','blue','green']
print(colors[0]) # red
print(colors[2]) # green
print(colors[-1]) # green
print(colors[-2]) # blue
print(colors[-3]) # red
cities = ['서울', '부산', '인천']
print (cities[:]) # a변수의 처음부터 끝까지 ,['서울', '부산', '인천']
print (cities[-4:4]) # 범위를 넘어갈 경우 자동으로 최대 범위를 지정 , ['서울', '부산', '인천']
print (cities[::2], " AND ", a[::-1]) # 2칸 단위로, 역으로 슬라이싱
print(cities[-1:1]) # =cities[2:1] = []
print(cities[-1:2]) # =cities[2:2] = []
print(cities[-1:3]) # =cities[2:3] = 범위를 넘어서므로 최대범위 ['인천']
a=[1,2,3]
b=[2,3,4]
b=a # 같은 주소를 가리키게 된다.
print(a) # [1,2,3]
print(b) # [1,2,3]
a[1]=4 # a의 값을 변경했지만 동일한 메모리주소를 갖는 b 값 역시 변경된다
print(a) # [1,4,3]
print(b) # [1,4,3]
그래서 일반적으로 다음과 같이 리스트의 원본을 건드리지 않고 복사본을 이용하는것을 권한다.
a=[1,2,3]
b=a[:] # Shallow copy
a[1]=4
print(a) # [1,4,3]
print(b) # [1,2,3]
그렇지만 다음과 같이 리스트 안에 리스트가 중첩된 경우 얕은 복사 로 해결되지 않는다.
그 이유는 리스트 안의 리스트 역시 메모리 주소값을 가지고 있고, 메모리 주소 값이 그대로 복사되었고 여전히 같은 메모리 주소를 가리키고 있다.
nested_list=[3]
a=[1,2,nested_list]
b=a[:] # Shallow copy
a[1]=4
print(a) # [1,4,[3]]
print(b) # [1,2,[3]]
nested_list[0]=5
print(a) # [1,4,[5]]
print(b) # [1,2,[5]]
그래서 이 문제를 해결하기 위해서는 copy
모듈의 deepcopy
를 이용한다.
import copy
nested_list=[3]
a=[1,2,nested_list]
b=copy.deepcopy(a) # Deep copy
print(a) # [1,2,[3]]
print(b) # [1,2,[3]]
nested_list[0]=5
print(a) # [1,2,[5]]
print(b) # [1,2,[3]]
패킹과 언패킹
패킹 | 언패킹 |
---|---|
한 변수에 여러개의 데이터를 넣는것 | 한 변수의 데이터를 각각의 변수로 변환하는것 |
t=[1,2,3] | a,b,c=t |
함수(Function)란 어떤 일을 수행하는 코드 덩어리로 하나의 논리적인 단위이다.
def function (parameter1,parameter2,...):
statements1
statements2
return value
function(argument1,argument2,...)
위의 함수 템플릿에서 parameter 와 argument의 차이는 다음과 같다.
parameter | argument |
---|---|
함수의 입력값 인터페이스 | 실제 parameter에 대입된 값 |
또한 위의 함수 템플릿에 따라 크게 4가지의 함수를 생각해 볼 수 있다.
_ | parameter 없음 | parameter 존재 |
---|---|---|
반환값 없음 | 함수내의 수행문만 수행 | parameter를 사용, 수행문만 수행 |
반환값 존재 | parameter 없이, 수행문 수행 후 결과값 반환 | parameter를 사용하여 수행문 수행 후 결과값 반환 |
기본적인 I/O 는 input() 과 print() 가 있다.
user_name=input('Enter your name : ') # user_input 은 문자열
user_age=int(input('Enter your age : ')) # user_age 는 정수
print(user_name,user_age)
파이썬은 3가지의 formatting 을 지원해준다.
% string
type | 설명 |
---|---|
%s | 문자열(String) |
%c | 문자(Character) |
%d | 정수(Integer) |
%f | 부동소수(Floating-point) |
%o | 8진수 |
%x | 16진수 |
%% | 문자 % 자체 |
print('%s %s' % ('one', 'two')
print('%d %d' % (1, 2)
print('Art : %5d, Price per Unit : %8.2f' % (453,59.058)) #Art : 453, Price per Unit : 59.06
print('Art : %05d, Price per Unit : %08.2f' % (453,59.058)) #Art : 00453, Price per Unit : 00059.06
print("I 'm {0} years old.".format(24))
print('Art : {0:5d}, Price per Unit : {1:8.2f}'.format(453,59.058)) #Art : 453, Price per Unit : 59.06
print('Art : {0:05d}, Price per Unit : {1:08.2f}'.format(453,59.058)) # Art : 00453, Price per Unit : 00059.06
name='lots-o'
age='NO!!'
print(f"Hello, {name}. You are {age}.")
print(f'{name:20}')
print(f'{name:>20}')
print(f'{name:*<20}') # 왼쪽 정렬, lots-o**************
print(f'{name:*>20}') # 오른쪽 정렬, **************lots-o
print(f'{name:*^20}') # 가운데 정렬, *******lots-o*******
number = 3.141592653589793
print(f'{number:.2f}') #3.14
프로그램 작성시 조건에 따른 판단 은 필수적인 부분이다. if
, elif
, else
를 통해 조건에 따라 참/거짓을 판단하여 동작을 제어할 수 있다.
if condition:
statements1
statements2
elif condition:
statements1
statements2
else:
statements1
statements2
조건의 참/거짓을 판단하기 위해서는 다음과 같은 비교 연산자들을 알고 있어야 한다.
비교연산자 | 비교상태 | 설명 |
---|---|---|
x < y | ~보다 작음 | x과 y보다 작은지 검사 |
x > y | ~ 보다 큼 | x과 y보다 큰지 검사 |
x == y | 같음 | x와 y가 같은지 검사 (값만 검사) |
x is y | 같음 | x와 y가 같은 객체 인지 검사 |
x != y | 같지 않음 | x와 y가 다른지 검사 (값만 검사) |
x is not y | 같지않음 | x와 y가 다른 객체 인지 검사 |
x >= y | 크거나 같음 | x과 y보다 이상인지 검사 |
x <= y | 작거나 같음 | x과 y보다 이하인지 검사 |
주의할것은 is
연산자의 경우 단순 값을 비교하는 것이 아닌 같은 객체, 즉 같은 메모리 주소를 갖는지 검사 한다는 것이다.
>>> a = [1,2,3]
>>> b = a
>>> c = [1,2,3]
>>> a is b
True
>>> a is c
False
또 한가지 주의해야할 점은 다음의 예시이다.
>>> a = -5
>>> b = -5
>>> a is b
True
>>> a = 256
>>> b = 256
>>> a is b
True
>>> a=-6
>>> b=-6
>>> a is b
False
>>> a=257
>>> b=257
>>> a is b
False
a=1 ,b=1
인 경우 다른 객체인데 True
, a=300,b=300
은 False
를 반환한다. 이것은 파이썬은 동작속도가 매우 느리기 때문에 로딩되는 순간에,-5~256
까지는 자주 사용하는 값들로 의 값을 메모리에 static 하게 저장한다. 따라서 변수 선언 시 값을 -5~256
안의 값으로 할당했을 때, 메모리를 새로 할당하는 것이 아니라 이미 저장된 메모리 주소를 할당하기 때문이다.
프로그램 작성시 반복 은 필수적인 부분이다. for
, while
, break
,continue
를 통해 반복을 제어할 수 있다.
for i in 'abcdefg':
if i == 'e':break
print(i)
######################
for i in range(1,10,2):
if i==3 : continue
print(i)
######################
for i in range(10,1,-1):
print(i)
######################
i=0
while i <5 :
print(i)
i+=1
######################
디버깅(Debugging) 은 코드의 오류를 발견하여 수정하는 과정이다. 즉,오류의 원인을 알고 그에 대한 해결책을 찾는것이다. 오류의 원인에는 다음 2가지가 있다.
우리 눈엔 컴퓨터가 문자들을 직접적으로 인식하는 것 같지만, 모든 데이터는 2진수로 인식된다. 이를 위해 2진수를 문자로 변환하는 표준 규칙을 정했는데 ASCII
,cp-949
,utf-8
,UNICODE
등 과 같은 규칙들이 존재한다. 그리고 변환된 문자열들은 메모리에 저장된다.
파이썬에서 문자열은 리스트와 같이 슬라이싱,인덱싱, 덧셈(Concatenation), in 연산 등 다양한 기능을 제공한다.
문자열 함수 | 기능 |
---|---|
len(a) | 문자열의 문자 개수를 반환 |
a.upper() | 대문자로 변환 |
a.lower() | 소문자로 변환 |
a.capitalize() | 첫 문자를 대문자로 변환 |
a.title() | 제목형태로 변환, 띄워쓰기 후 첫 글자만 대문자 |
a.count('abc') | 문자열 a에 'abc'가 들어간 횟수 반환 |
a.find('abc') | 문자열 a에 'abc'가 왼쪽 기준 처음 발견되는 위치(오프셋) 반환 |
a.rfind('abc') | 문자열 a에 'abc'가 오른쪽 기준 처음 발견되는 위치(오프셋) 반환 |
a.startswith('ab'c) | 문자열 a는 'abc'로 시작하는 문자열여부 반환 |
a.endswith('abc') | 문자열 a는 'abc'로 끝나는 문자열여부 반환 |
a.strip() | 좌우 공백을 없앰 |
a.rstrip() | 오른쪽 공백을 없앰 |
a.lstrip() | 왼쪽 공백을 없앰 |
a.split() | 공백을 기준으로 나눠 리스트로 반환 |
a.split('abc') | abc를 기준으로 나눠 리스트로 반환 |
a.isdigit() | 문자열이 숫자인지 여부 반환 |
a.islower() | 문자열이 소문자인지 여부 반환 |
a.isupper() | 문자열이 대문자인지 여부 반환 |
Raw string 은 escape문, 예를들어 \b(백스페이스) , \" (큰따옴표 출력) , \n(줄바꿈) 등,을 무시하고 있는 그대로 출력해준다.
>>> raw_string ="성장하자\n 제발"
>>> print(raw_string)
성장하자
제발
>>> raw_string = r"성장하자\n 제발"
>>> print(raw_string)
성장하자\n 제발
변수가 사용되는 범위는 다음 두가지가 있다.
def function(t):
t=20 # 새로운 지역변수
print(t) # 20
x=10 # 전역변수
function(x)
print(x) # 10
def function():
s = "I love London!" # 새로운 지역변수
print(s) # I love London
s = "I love Paris!"
function()
print(s) # I love Paris
전역변수는 함수에서 사용가능 하지만, 함수내에서 전역변수와 같은 이름의 변수를 선언하면 새로운 지역변수가 생긴다. 만약 함수 내에서 전역변수를 사용하고 싶다면 global
키워드를 사용한다.
def function():
global s
s = "I love London!" # 전역변수
print(s) # I love London
s = "I love Paris!"
function()
print(s) # I love London
일반적으로 함수에서 parameter를 전달하는 방식은 크게 두가지이다.
그런데 파이썬은 특이하게도 위의 두 경우에 해당하지 않고 Object Reference 를 넘겨준다. 이러한 파이썬의 함수 parameter 전달 방식을 Call by Object Reference 또는 Call by Sharing 이라 한다.
파이썬에서는 global 인지 loacl 인지 영역에 따라 변수들의 정보를 저장하는 namespace가 따로 있다. 즉, 전역변수를 함수에서 인자로 받아오더라도 함수내에서는 지역변수(이름표)에 불과하다. 그래서 다음과 같은 경우 Call by Value 처럼 행동한다.
def function(L):
L=[1,2,3] # 새로운 지역변수, Call by value 처럼 행동하여 L에 영향을 끼치지 않는다.
print(L) # [1,2,3]
L=[1,2,4]
function(L) # [1,2,3]
print(L) # [1,2,4]
주의할점은 다음과 같이 단순히 지역변수 생성이 아닌, 직접 조작하는 경우 는 Call by Reference 처럼 행동한다.
def function(L):
L.append(5) # Mutable 객체를 직접 조작하는 경우
print(L) # [1,2,4,5]
L=[1,2,4]
function(L) # [1,2,4,5]
print(L) # [1,2,4,5]
요약하면, 파이썬은 단순히 함수내에서 새로운 객체를 생성하는 경우에는 Call by Value 처럼 행동하지만, 직접 조작하는 경우에는 Call by Reference 처럼 행동한다.(물론 이때, 직접 조작하기 위해선 Mutable 객체여야 한다.)
참고
Mutable | Immutable |
---|---|
값 변경가능 | 값 변경 불가능 |
list,str,dict | int,float,bool,tuple,str,frozenset |
(a=4
에서 a=5
로 값이 변경된거니까 int 객체는 mutable 한거 아닌가요? 라는 생각을 할 수 있지만, 정확하게 말하면 값이 변경된 것이 아닌 새로운 객체로 바인딩 된것이다.)
>>> a=4
>>> id(a)
140708198164352
>>> a=5
>>> id(a)
140708198164384
재귀함수(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: "))))
파이썬은 dynamic typing 으로 처음 함수를 사용하는 사용자가 인터페이스를 파악하기 어렵다는 단점이 있다. 그래서 python 3.5 이후로 pep484에 기반하여 type hints 기능 제공
def do_function(var_name: var_type) -> return_type:
pass
파이썬 클래스 또는 함수에 대한 상세스팩을 사전에 작성하여 사용자의 이행도를 올려준다.
명확한 규칙은 없지만 팀 또는 프로젝트 단위로 사람의 이해를 돕기 위해 일관성 있게 코드를 작성하는 것이 좋은 코드 작성이다. 최근에는 black
모듈을 활용하여 pep8 like 수준을 준수하기도 한다.