설치 1 2 3 4 5 6 7 8 brew install python@3.10 export PATH=$PATH :/opt/homebrew/binalias python3=python3.10alias python=python3.10alias pip3=pip3.10alias pip=pip3.10
1 2 3 /usr/bin/python3 -V Python 3.9.6
자료형 1 2 3 4 5 6 a = 'python' name: str = "Alice" age: int = 30 height: float = 5.7 is_student: bool = True byte_str: bytes = b"Hello, World!"
python 에서는 원시타입 개념이 없고 모두 객체로 취급된다.
1 2 3 4 5 class int (object ): """ int([x]) -> integer int(x, base=10) -> integer
문자열 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 a: str = 'python' a * 2 "=" * 50 'hi' + 3 a[3 ] a[-2 ] "hello %s" % "wolrd" "hello %02d" % 3 "hello {0}" .format ("world" ) "my name is {name} and {age} age" .format (name="kouzie" , age=20 ) greeting = f"my name is {name} and {age} age" string_list = ["test" , "print" , "hello" ] print ('_' .join(string_list))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 a[0 :3 ] a[:3 ] a[3 :] a[1 :-1 ] a: str = 'python' print (a[::1 ]) print (a[::2 ]) print (a[::3 ]) print (a[::-1 ]) print (a[::-2 ])
typing python 3.9(2019) 이전 버전에서는 list, tuple, dict, set 에 요소타입을 명시할수 없어 타입힌트를 쓸수 없었다. 그래서 아래와 같이 typing 에서 제공하는 제너릭타입 클래스를 사용해야 했다.
1 2 3 4 5 6 from typing import List , Tuple , Dict , Set my_list: List [int ] = [1 , 2 , 3 ] my_tuple: Tuple [int , str ] = (1 , "hello" ) my_dict: Dict [str , int ] = {"one" : 1 , "two" : 2 } my_set: Set [int ] = {1 , 2 , 3 }
python 3.9 이후부터는 기본 빌트인 자료형에서 제공하는 자료형에서도 타입힌트를 쓸 수 있게 되었다.
1 2 3 4 my_list: list [int ] = [1 , 2 , 3 ] my_tuple: tuple [int , str ] = (1 , "hello" ) my_dict: dict [str , int ] = {"one" : 1 , "two" : 2 } my_set: set [int ] = {1 , 2 , 3 }
list 1 2 3 4 5 6 7 b: list [int ] = [4 , 5 , 6 ] b: list [int ] = list (map (lambda x: x ** 2 , b)) print (b) print (list ("10010101" ))
list([iterable]) 함수를 사용해 반복 가능한 객체(str, tuple, dict, set, range) 를 list 화 할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 a: list [int ] = [1 ,2 ,3 ] b: list [int ] = [4 ,5 ,6 ] print (a+b) print (a*2 ) a.extend(b) print (a) c = [1 , 2 , 3 ] d = [4 , 5 , 6 ] e = [*c, *d] print (e)
1 2 3 4 b = ['a' , 'b' , 'c' , 'd' , 'c' , 'c' ] b.index('c' ) b.count('c' )
1 2 3 4 5 my_list = [1 , 2 , 3 , 4 , 5 ] print (my_list[-1 ]) print (my_list[-2 ]) print (my_list[-3 ])
1 2 3 4 a = [1 ,2 ,3 ] a.append(4 ) a.insert(0 ,5 )
1 2 3 4 5 6 7 8 b = [4 , 5 , 6 , 7 ] b.remove(5 ) del b[2 :] r = b.pop() b = [4 , 5 , 6 , 7 ] b.pop(-2 )
1 2 3 4 5 6 a = [2 ,3 ,1 ,4 ] b = ['b' ,'c' ,'a' ,'d' ] a.sort() b.sort() b.reverse()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 my_list = [0 , 1 , 2 , 3 , 4 , 5 ] print (my_list[:1 ]) print (my_list[:2 ]) print (my_list[:-1 ]) print (my_list[0 :]) print (my_list[1 :]) print (my_list[1 :1 ]) print (my_list[1 :2 ]) print (my_list[1 :3 ]) print (my_list[::2 ]) print (my_list[::-1 ])
dict 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 a: dict [str ] = {"hi" : "hello" } a["who" ] = "me" del a['hi' ] a.clear() a = {'hi' : 'hello' , 'who' : 'me' } a.keys() a.values() a.items() list (a.keys()) list (a.values()) list (a.items()) items = a.items()[0 ][0 ]
tuple, set tuple: immutable list, () 기호를 사용해 생성한다. set: 중복없는 list, 집합자료형, {} 기호를 사용해 생성한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 t: tuple [int ] = (1 ,2 ,3 ) s: set [int ] = {1 ,2 ,3 } t = tuple ([1 ,2 ,3 ]) s = set ([1 ,2 ,3 ]) print (t[0 ]) s1: set [int ] = {1 ,2 ,3 ,4 } s2: set [int ] = {3 ,4 ,5 ,6 } s1 & s2 s1 | s2 s1 - s2 s1 = {1 ,2 ,3 ,4 } s1.add(5 ) s1.update([6 ,7 ,8 ]) s1.remove(3 ) s1.pop()
typing - 고급타입힌트 Union, Optional, Any 와 같은 고급 타입 힌트를 사용할 경우 여전히 typing 을 사용해야한다.
Union Union은 여러 타입을 하나로 결합하여 사용, 변수나 함수 인자가 여러 타입을 가질 수 있음을 나타낼 때 사용한다.
1 2 3 4 5 6 7 8 def process (data: Union [int , str ] ) -> None : if isinstance (data, int ): print (f"Processing an integer: {data} " ) else : print (f"Processing a string: {data} " ) process(10 ) process("hello" )
python 3.10 이후부턴 | 연산자를 통해 Union 대체가 가능하다.
1 2 def toString (num: int | float ) -> str : return str (num)
Optional Python 에서 null 을 뜻하는 None 타입과 같이 사용된다.Optional[str] 는 Union[str, None]과 동일.
1 2 3 x = None print (x) print (type (x))
1 2 3 4 5 6 7 8 9 10 from typing import Optional def greet (name: Optional [str ] = None ) -> str : if name is None : return "Hello, Stranger!" else : return f"Hello, {name} !" print (greet()) print (greet("Alice" ))
Any 1 2 3 4 5 6 7 8 from typing import Any def process (data: Any ) -> None : print (f"Processing data: {data} " ) process(10 ) process("hello" ) process([1 , 2 , 3 ])
Type, TypeVar Type: 타입 힌트를 작성할 때 사용 TypeVar: 제네릭 타입을 정의할 때 사용
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 from typing import Type , TypeVarclass Animal : pass class Dog (Animal ): pass T = TypeVar('T' ) A = TypeVar('A' , bound=Animal) def create_instance (cls: Type [A] ) -> A: return cls() def get_first_element (lst: list [T] ) -> T: return lst[0 ] if __name__ == '__main__' : dog_instance = create_instance(Dog) int_list = [1 , 2 , 3 ] str_list = ['a' , 'b' , 'c' ] first_int: int = get_first_element(int_list) first_str: str = get_first_element(str_list) print (first_int, first_str)
제어문 if, elif, else 1 2 3 4 5 6 7 8 x = True y = False if x: print ("hi" ) elif y: print ("hello" ) else : print ("world" )
bool 자료형 끼리는 and, or, not
리스트, 튜플을 검사할 땐 in, not in
1 2 1 in [1 ,2 ,3 ] True 1 not in [2 ,3 ,4 ] True
삼항연산자 1 2 3 4 5 6 7 8 x = 10 y = 20 result = "x is greater" if x > y else "y is greater or equal" print (result)
while 1 2 3 4 5 while x: if y: break if z: continue
파이썬에는 do while 없음
for - list, tuple, dict 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 for item in [1 , 2 , 3 , 4 , 5 ]: print (item) for item in (1 , 2 , 3 , 4 , 5 ): print (item) person = {"name" : "Alice" , "age" : 30 , "city" : "New York" } for key, value in person.items(): print (f"{key} : {value} " ) for key in person: print (key) for value in person.values(): print (value)
1 2 3 for char in "hello" : print (char)
for - range, enumerate, zip 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 for i in range (5 ): print (i) for i in range (1 , 6 ): print (i) for i in range (1 , 10 , 2 ): print (i) names: list [str ] = ["Alice" , "Bob" , "Charlie" ] scores: list [int ] = [85 , 90 , 95 ] for name in enumerate (names): print (f"Name: {name} " ) for name, score in zip (names, scores): print (f"Name: {name} , Score: {score} " )
참고로 range, enumerate, zip 는 함수 아니고 iterable 가능한 객체임
1 2 3 4 5 6 names: list [str ] = ["Alice" , "Bob" , "Charlie" ] scores: list [int ] = [85 , 90 , 95 ] my_enumerate: enumerate [str ] = enumerate (names) my_zip: zip = zip (names, scores) my_range: range = range (5 )
for - 컴프리헨션
출처: https://dojang.io/mod/page/view.php?id=1322
1 2 3 4 5 6 7 8 9 10 11 12 13 squares = [] for x in range (10 ): squares.append(x**2 ) squares = [x**2 for x in range (10 )] print (squares) th_squares = [x ** 2 for x in range (10 ) if x % 3 == 0 ] print (th_squares)
1 arr = [[0 ] * (N + 1 )] + [[0 ] + list (map (int , input ().split())) for _ in range (N)]
컴프리헨션은 아래와 같은 형태로 구성된다.
1 2 3 list = [{expression} {for item in iterable} {if condition}] 진행 순서: {for item in iterable} → {if condition} → {expression}
1 2 3 my_list = [i * j for j in range (2 , 10 ) for i in range (1 , 10 )] print (len (my_list))
1 ~ 9, 2 ~ 9 두개 숫자 조합으로 총 72개의 요소가 생성될 수 있다.
보통 아래와 같이 2차원 배열을 만들 때 많이 사용한다.
1 2 matrix = [[i * j for j in range (3 )] for i in range (3 )] print (matrix)
list 외에도 dict, set 에서도 컴프리헨션 문법 적용이 가능하다.
1 2 3 4 5 6 7 square_dict = {x: x**2 for x in range (5 )} print (square_dict) square_set = {x**2 for x in range (10 )} print (square_set)
아래와 같이 코테에서 2차원 배열 생성할 때 자주 사용함.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import ioimport sysinput_str = """3 3 1 2 3 4 5 6 7 8 9 """ sys.stdin = io.StringIO(input_str) N, M = map (int , input ().split()) arr = [list (map (int , input ().split())) for _ in range (N)] print (N, M)print (arr)
match, case python 3.10 이후 추가된 문법, switch 문 역할을 수행하는 제어문 이전에는 모두 if-elif-else 로 처리했어야함.
함수 1 2 3 4 5 6 7 8 9 def add1 (a=1 , b=1 ): return a + b def add2 (a=1 , b=1 ) -> int : return a + b add1() add2(b=2 ,a=5 )
python 은 별도의 반환값, 매개변수의 인자값 등을 생략해도 된다.
함수 또한 객체로 취급되며 다양한 메타데이터들을 가지고 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def foobar (): """foo~~bar~~""" print ("baz" ) if __name__ == '__main__' : print (type (foobar)) print ("Docstring:" , foobar.__doc__) print ("Name:" , foobar.__name__) print ("Module:" , foobar.__module__) print ("Defaults:" , foobar.__defaults__) print ("Code:" , foobar.__code__) print ("Globals:" , foobar.__globals__) print ("Closure:" , foobar.__closure__) print ("Annotations:" , foobar.__annotations__) print ("KW Defaults:" , foobar.__kwdefaults__) print ("Dict:" , foobar.__dict__)
가변인자, 키워드인자 가변 인자
1 2 3 4 5 def add_all (*args ): result = 0 for i in args: result += i return result
키워드인자
1 2 3 4 5 6 7 8 def example_function (**kwargs ): for key, value in kwargs.items(): print (f"{key} : {value} " ) example_function(name="Alice" , age=30 , city="New York" )
가변인자 + 키워드인자
1 2 3 4 5 6 7 8 9 10 def example_function (*args, **kwargs ): print ("Positional arguments:" , args) print ("Keyword arguments:" , kwargs) print (type (args)) print (type (kwargs)) example_function(1 , 2 , 3 , name="Alice" , age=30 )
python 문법에서 가변인자는 항상 키워드인자 앞에 있어야 한다.
파이썬에는 메서드오버로딩 을 지원하지 않기때문에 생성자나 함수에 여러개의 변수를 받아야 할 경우 *args, **kwargs 를 매개변수를 적극적으로 사용해야한다.
map 고차 함수 map 의 결과로 생성되는 iterator 객체. 데이터 처리의 지연 평가(lazy evaluation) 특성을 가지고 있어 메모리 효율적이다.
1 2 result = map (int , "10010101" ) print (type (result))
아래와 같이 str 로 반환되는 문자열을 int 로 변환하여 map 객체로 변환하고 list 로 넘겨 쉽게 숫자 list 생성이 가능하다.
1 print (list (map (int , "10010101" )))
한 번만 소비 가능하여 한 번 순회하거나 변환하면 소진된다.
1 2 3 4 5 nums = [1 , 2 , 3 ] squared = map (lambda x: x**2 , nums) print (list (squared)) print (list (squared))
for 컴프리핸션은 한번에 모든 요소를 순회해야 하지만 map 의 경우 요청시에 연산이 가능하기 때문에 좀더 효율적이다.
1 2 3 4 5 6 nums = range (1 , 1_000_000 ) squared = map (lambda x: x**2 , nums) print (next (squared)) print (next (squared))
람다, filter, reduce, sorted 1 2 3 add = lambda x, y: x + y print (add(3 , 5 ))
1 2 3 add = lambda x, y: x + y print (add(3 , 5 ))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 numbers = [1 , 2 , 3 , 4 , 5 ] squared = list (map (lambda x: x ** 2 , numbers)) print (squared) numbers = [1 , 2 , 3 , 4 , 5 , 6 ] evens = list (filter (lambda x: x % 2 == 0 , numbers)) print (evens) from functools import reducenumbers = [1 , 2 , 3 , 4 , 5 ] product = reduce(lambda x, y: x * y, numbers) print (product) points = [(1 , 2 ), (3 , 1 ), (5 , 7 ), (2 , 3 )] sorted_points = sorted (points, key=lambda point: point[1 ]) print (sorted_points)
고차함수 - 함수를 인자로 1 2 3 4 5 6 7 8 9 10 11 def apply_function (func, x, y ): return func(x, y) def add (a, b ): return a + b if __name__ == '__main__' : result = apply_function(add, 2 , 3 ) print (result)
typing 의 Callable 을 사용하면 타입힌트를 적용할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 def apply_function (func: Callable [[int , int ], int ], x: int , y: int ) -> int : return func(x, y) def add (a: int , b: int ) -> int : return a + b if __name__ == '__main__' : result1 = apply_function(add, 4 , 5 ) print (result1) result2 = apply_function(lambda x, y: x * y, 4 , 5 ) print (result2)
변수 scope 파이썬도 타 언어와 같이 변수의 정의된 위치에 따라 scope 를 가지는데, global, local 두개로 나뉜다.
global : 모듈단위
local : 함수단위
함수 중첩시 nonlocal scope 도 존재하긴 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 global_var = "전역 변수" def outer (): nonlocal_var = "비전역 변수" print (global_var) print (nonlocal_var) def inner (): local_var = "지역 변수" print (global_var) print (nonlocal_var) print (local_var) print (local_var) print (nonlocal_var) print (local_var)
이때 scope 를 명확히 지정하기 위해 키워드를 사용가능
1 2 3 4 5 6 7 8 9 global_var = "전역 변수" def outer (): global global_var global_var = "새로운 전역 변수" print (global_var) outer() print (global_var)
당연히 scope 를 지정하지 않으면 새로운 변수가 선언되면서 초기화 되지 않음
nonlocal 키워드 역시 새로운 변수 선언을 하지 않고 상위 중첩함수의 변수를 가져오기 위한 키워드
closure 중첩함수를 사용해 closure 를 생성할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def make_multiplier (factor ): def multiplier (number ): return number * factor return multiplier if __name__ == '__main__' : double = make_multiplier(2 ) triple = make_multiplier(3 ) quadruple = make_multiplier(4 ) print (double(5 )) print (triple(5 )) print (quadruple(5 ))
decorator python 2.2 도입, 다른 함수를 인수로 받아 새롭게 수정된 함수로 대체하는 고차함수
주로 권한검사, 로깅 같이 함수안에서 고정반복되는 코드를 작성해야할 때 자주 사용한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def my_decorator (func ): def wrapper (): print ("Something is happening before the function is called." ) func() print ("Something is happening after the function is called." ) return wrapper @my_decorator def foo (): print ("Hello!" ) if __name__ == '__main__' : foo()
초기 foo() 함수는 그대로 있고 my_decorator() 함수를 겨쳐 foo() 가 재정의된다. 실제 우리가 정의했던 foo() 함수는 재정의된 함수 별도의 공간에 참조변수로서 존재하게 된다.
일반적으로 정의한 함수의 매개변수 타입, 개수 모두 다르기때문에 decorator 함수 정의시 wrapper함수 를 사용하는게 대부분
가변인자와 가변키워드 인자를 통해 wrapper 함수 정의가 가능하다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def check_is_admin (func ): def wrapper (*args, **kwargs ): if kwargs.get('username' ) != 'admin' : raise Exception("username is not admin, name:%s" % kwargs.get('username' )) return func(*args, **kwargs) return wrapper @check_is_admin def test_func (msg: str , **kwargs ): print (msg) if __name__ == '__main__' : test_func('hello world' , username="admin" , age=30 )
변환되는 과정을 코드로 나타내면 아래와 같다. 이미 재정의된 함수를 다시 재정의하기 때문에 이중으로 재정의되어 아래같이 실행된다.
1 test_func = check_is_admin("admin" )(test_func)
update_wrapper 1 2 3 4 5 6 7 8 9 10 11 12 13 14 def test_deco (f ): def wrapper (*args, **kwargs ): print ("hello" ) return f(*args, **kwargs) return wrapper @test_deco def foobar (): """foo~~bar~~""" print ("boo" ) if __name__ == '__main__' : print (foobar.__doc__) print (foobar.__name__)
한번 감싸는 순간 함수가 재정의 되기때문에 기존 함수의 메타데이터들(속성, 이름) 들을 잃어버린다. 파이썬 자동 문서화등에서 문제가 발생하수 있기때문에 원복해주는 functools 패키지의 update_wrapper() 함수를 사용하는 것을 권장
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import functoolsdef test_deco (f ): def wrapper (*args, **kwargs ): return f(*args, **kwargs) wrapper = functools.update_wrapper(wrapper, f) return wrapper @test_deco def foobar (): """foo~~bar~~""" print ("goo" ) if __name__ == '__main__' : print (foobar.__doc__) print (foobar.__name__)
update_wrapper 내부 코드를 보면 각종 메타데이터 정보를 별도로 내부변수에 담아두었다가 래퍼 함수 참조변수에 할당하도록 되어있다.
한줄 추가하는것 간단한 일이지만 좀더 좋은 직관성, 짧은 코드를 위해 update_wrapper 기능을 하는 decorator 용 함수가 정의되어 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import functoolsdef test_deco (f ): @functools.wraps(f ) def wrapper (*args, **kwargs ): return f(*args, **kwargs) return wrapper @test_deco def foobar (): """foo~~bar~~""" print ("goo" ) if __name__ == '__main__' : print (foobar.__doc__) print (foobar.__name__)
decorator 매개변수 아래와 같이 3중첩으로 decorator 함수를 정의하면 decorator 자체에 매개변수 전달이 가능하다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 def check_is_admin_mapping (name ): def decorator (func ): @functools.wraps(func ) def wrapper (*args, **kwargs ): if kwargs.get('username' ) != name: raise Exception("username is not {0}, name:{1}" .format (name, kwargs.get("username" ))) else : return func(*args, **kwargs) return wrapper return decorator @check_is_admin_mapping("kouzie" ) def test_func_kouzie (msg: str , **kwargs ): print (msg) @check_is_admin_mapping("admin" ) def test_func_admin (msg: str , **kwargs ): print (msg) if __name__ == '__main__' : test_func_kouzie("hello" , username="kouzie" , age=30 ) test_func_admin("hello" , username="admin" , age=30 )
다중 decorator 여러번 decorator 적용할 경우 메서드 재정의가 한번 더 이루어지면서 two depth decorator 를 통해 원본 함수를 호출하게된다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 def check_is_admin_mapping (name ): def decorator (func ): @functools.wraps(func ) def wrapper (*args, **kwargs ): if kwargs.get('username' ) != name: raise Exception("username is not {0}, name:{1}" .format (name, kwargs.get("username" ))) else : return func(*args, **kwargs) return wrapper return decorator def check_is_age_mapping (age ): def decorator (func ): @functools.wraps(func ) def wrapper (*args, **kwargs ): if kwargs.get('age' ) < age: raise Exception(f"age is before, age:{kwargs.get('age' )} " ) else : return func(*args, **kwargs) return wrapper return decorator @check_is_admin_mapping("kouzie" ) @check_is_age_mapping(20 ) def test_func (msg: str , **kwargs ): print (msg) if __name__ == '__main__' : test_func("hello" , username="kouzie" , age=30 )
클래스와 decorator 클래스에 decorator 를 사용해 클래스를 조작하는 방법.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 import uuiddef get_name_id (self ): return str (self .name) + str (self .uuid) def set_class_name_and_id (klass ): klass.name = str (klass) klass.uuid = uuid.uuid4() klass.get_name_id = get_name_id return klass @set_class_name_and_id class SomeClass (object ): pass if __name__ == '__main__' : sc = SomeClass() print (SomeClass.name) print (sc.uuid) print (sc.get_name_id())
클래스변수, 메서드를 decorator를 통해 추가가능하다
함수가 클래스로 재정의되어 클래스의 __call__ 함수를 호출하게되는 형태로 변환