목차




Variables

특징

  • 값을 저장하는 장소
  • 변수는 메모리 주소를 가지고 변수에 들어가는 값은 메모리 주소에 할당됨
  • 선언 되는 순간 메모리 특정영역에 물리적인 공간이 할당되는 것

변수명 규칙

  • 알파벳, 숫자, 언더바로 작성 가능
  • 의미있는 단어로 표기하는 것이 좋음
  • 대문자와 소문자는 다르게 구분됨
  • 특별한 의미가 있는 예약어는 변수명으로 안 됨

기본 자료형

  • 수치 자료형
    • Integer, 정수형 → a = 1
    • float, 실수형 → a = 1.0
  • 문자형
    • string → a = “1”
  • 논리 / 불린 자료형
    • boolean → a = True

Dynamic Typing

: 변수 선언할 때 자료형을 안적어도 실행 시점에 알아서 알맞는 자료형으로 저장함, 인터프리터 언어라서 가능

연산자와 피연산자

  • 연산자 (Operator)
    • +, -, *, / 와 같은 기호들
  • 피연산자 (Operand)
    • 연산되는 숫자나 변수

List (또는 Array)

  • 여러 데이터를 한 번에 관리하는 자료형
  • 파이썬 리스트의 특징은 다른 언어와 다르게 다른 자료형도 하나의 리스트 안에 넣을 수 있다는 것
  • 인덱싱
    • 리스트의 원소에 접근하는 방법
    • arr = [2, 3, 4] 에서 첫 원소 참고하려면 arr[0] 하면 됨
  • 슬라이싱
    • 리스트에서 원하는 구간의 원소들을 한번에 참고하는 방법
    • 만약 arr = [2, 3, 4] 에서 arr[-8:] 한다면? → 없는 인덱스 무시하고 있는만큼, 처음부터 2 3 4 가져옴
  • 내부 함수
    • append(x) : x를 마지막에 추가
    • pop() : 마지막 원소 제거
    • extend([1, 2]) : 1, 2 를 뒤에 확장, 리스트 + 리스트 형태
    • remove(x) : x 값 삭제
    • del arr[0] : 0 번째 원소 삭제

sort 와 sorted

  • arr.sort()
    • 리스트 내장함수로 리턴 없이 리스트를 정렬시킴
  • sorted(arr)
    • 파라미터로 리스트를 넣고 정렬시킨 리스트를 리턴함
    • 원형에는 변화 없음

리스트 메모리 저장 방식

  • 리스트끼리 = 연산자를 사용하면 값이 아닌 메모리 주소를 연결시켜줌 (얕은 복사)
  • 딥카피는 값만 베끼는 방식으로 나중에 배움
  • 간단하게 값만 베끼려면 b = a[:] 하면 값만 가져감
a = [5, 4, 3, 2, 1]
b = [1, 1, 1, 1, 1] 
b = a
print(b) # [5, 4, 3, 2, 1]
a.sort
print(b) # [1, 2, 3, 4, 5]
b = [5, 5, 5, 5, 5]
print(a, b) # [1, 2, 3, 4, 5] [5, 5, 5, 5, 5]

패킹과 언패킹

  • 패킹 : 한 변수에 여러 개의 데이터를 넣는 것
    • t = [1, 2, 3]
  • 언패킹 : 한 변수의 데이터를 각각의 변수로 반환
    • a, b, c = t

이차원 리스트

  • 리스트 안에 리스트 넣음
  • 이차원 리스트를 복사하는 방법은?
    • import copy, b = copy.deepcopy(a)
    • 그냥 b = a 하면 메모리를 같이해서 하나 바뀌면 다같이 바뀜




Function and Console I/O

함수 선언

def 함수 이름(parameter #1, … , ):

수행문 #1(statements)

return 반환값

  1. % string formatting (old-school)
    • print(‘%s %s’ % (“ab”, “cd”))
    • print(“{} {}” % (“a”, “b”))
    • print(“%5d %8.2f” % (12, 12.3456)) → 12 (5칸 확보) 12.35 (닷까지 포함해서 8칸 확보 + 2자리수까지 나타냄)
  2. format 함수
    • print(“{0}, {1}”.format(“apple”, 1) → apple, 1
    • padding
      • print(“{0<10s}, {1>10.3f}”.format(“apple”, 5.243) → apple (10칸 확보, 왼쪽 정렬), 5.243(10칸 확보, 오른쪽 정렬)
  3. f-string
name = "HS"
age = 27
print(f"Hello, {name}. I'm {age} years old.")
print(f"{name:20}") # 왼쪽 정렬이 기본, 20칸 확보
print(f"{name:>20}") # 오른쪽 정렬, 20칸
print(f"{name:*<20}") # 왼쪽 정렬하되 빈칸은 *로 채움
print(f"{name:*^20}") # 가운데 정렬하되 나머지 *로 채움
# *********HS*********

n = 3.141592
print(f"{n:.2f}") # 3.14




Conditionals and Loops

조건문

  • 조건에 따라 특정한 동작을 하게하는 명령어
  • 조건을 나타내는 기준과 실행해야 할 명령으로 구성됨
  • if, else, elif 등 예약어 사용
  • 반드시 들여쓰기 해야함
  • 조건 반단 방법
    • x < y
    • x > y
    • x == y
    • x is y (메모리 주소 같은지 검사)
    • x ≠ y
    • x is not y (메모리 주소 다른지 검사)
    • x ≥ y
    • x ≤ y
  • if “a” 는 True, if “” 는 False
  • and, or, not 과 함께 사용
  • all(bool_arr) : 불리언 리스트 안 값이 모두 트루인가
  • any(bool_arr) : 불리언 리스트 안 값에 트루가 있는가
  • 삼항 연산자
    • c = 2 if a == 1 else 3

반복문

  • 정해진 동작을 반복적으로 수행하게하는 명령어
  • 조건과 들여쓰기 필수, 무한루프 조심!
  • for 문
    • 반복 실행 횟수를 명확히 알 때 사용
    • range(a, b) → a 부터 b-1 까지 반복
      • range는 제너레이터
      • list(range(5)) → [0, 1, 2, 3, 4]
    • 역순 가능, for i in range(10, 0, -1): → 10부터 1까지 1간격으로 반복
  • while 문
    • 반복 실행 횟수를 명확히 모를 때 사용
    • 조건 만족하는동안 반복
  • 반복 제어 break(반복문 끝냄), continue(해당 회차 그만 돌고 다음 회차 수행)

디버깅

  • 문법적 오류를 찾기 위해 에러메시지를 잘 분석해야함
  • 논리적 오류를 찾기 위해 테스트를 잘해야함

모듈로 실행

  • if name == “main” 은 왜쓸까?

    a.py 코드 안에 위와 같은 네임이 있다면, a.py 를 직접 실행하면 main 이 실행되고, 다른 코드 b.py 에서 a.py 의 일부 함수만 사용했다면 a의 함수는 실행되지 않는다.




String and advanced function concept

String (문자열)

  • 한 글자당 1 바이트씩 글자를 저장
    • 1 바이트 = 8 비트 = 2^8 = 256 까지 저장 가능
    • 하나의 문자는 숫자 (아스키 코드) 로 변형돼서 표현됨 by utf-8
  • 시퀀스형 자료형이라 리스트랑 비슷함
    • 인덱싱, 슬라이싱 등 가능
  • 내장함수
    • lower() : 소문자로 변경
    • isdigit() : 모두 숫자 (10진수) 인지 확인하고 트루 올 폴스 리턴
    • 등등 다양한 함수 존재
  • 따옴표를 작성하고 싶다면? ' 로 표시
  • raw_string = r”Hi \n” 하고 출력하면 문자 그대로 출력됨

Call by object reference

함수에서 파라미터 넘기는 방식

  1. Call by Value
    함수에 인자를 넘길 때 값만 넘김. 함수 내에서 인자 값을 변경해도 영향 안줌
  2. Call by Reference
    함수에 인자를 넘길 때 메모리 주소를 넘김. 함수내에서 인자 값 변경하면 호출자의 값도 변경됨
  3. *Call by Object Reference (파이썬 방식)
    객체의 주소가 함수로 전달되는 방식. 전달된 객체를 참조하여 변경시 호출자에게 영향을 주나, 새로운 객체를 만들 경우 호출자에게 영향을 안 줌
def spam(eggs):
	eggs.append(1)
	eggs = [2, 3]
	print(eggs) # [2, 3]

ham = [0]
spam(ham)
print(ham) # [0, 1]

변수의 범위

  • 전역 변수와 로컬 변수가 이름이 같더라도 다른 객체로 취급함
  • 같게 쓰고 싶으면 로컬 범위에 global 로 선언을 해줘야 함

재귀함수

  • 자기 자신을 호출하는 함수
  • 종료 조건까지 자기를 호출함

Type hints

  • 다이나믹 타이핑은 편하지만 처음 함수를 사용하는 사용자가 인터페이스를 알기 어려움

    → type hints (파이썬 3.5 이후) 제공

      a: int = 4
      arr = []
    
      def insert(a: int) -> None: 
      	arr.append(a)
    
  • 장점

    • 사용자에게 인터페이스를 명확히 알려줄 수 있음
    • 시스템에 안정성을 더함

Docstring

  • 파이썬 함수에 대한 상세스펙을 사전에 작성 → 함수 사용자의 이행도 증가
  • 함수명 아래에 세개의 따옴표로 docstring 영역 표시

(앞으로 코딩할때 무조건 적는 습관을 기르자!)

함수 작성 가이드 라인

  • 가능하면 짧게 (줄 수를 줄이자)
  • 함수 이름에 함수의 역할, 의도가 명확히 드러날 것 (이름은 동사먼저)
  • 하나의 함수에는 유사한 역할을 하는 코드만 포함 (add 라고 했으면 출력은 지양)
  • 인자로 받은 값 자체를 바꾸진 말 것 (원래 호출값 변화 위험이 있기 때문에 임시변수로 복사해서 쓸 것)
  • 함수는 언제 만드는가?
    • 공통적으로 사용되는 코드는 함수로 변환
    • 복잡한 수식 → 식별 가능한 이름의 함수로 변환
    • 복잡한 조건 → 식별 가능한 이름의 함수로 변환

코딩은 팀플

사람을 위한 코드

  • 컴퓨터가 이해하는 코드는 누구나 짬, 사람이 이해할 수 있는 코드를 짜야 좋은 프로그래머 -마틴 파울러-
  • 코드는 하나의 보고서.

⇒ 규칙이 필요함 ⇒ 코딩 컨벤션

  • 명확한 규칙은 없고 팀이 정함
  • 들여쓰기는 4 space
  • 한 줄은 최대 79자까지
  • 불필요한 공백은 피함
  • 연산자는 1칸 이상 안띄움
  • 주석은 항상 갱신, 불필요한 주석 삭제
  • 코드의 마지막에는 항상 한 줄 추가
  • 소문자 l, 대문자 O, 대문자 I 금지
  • 함수명은 소문자로, 밑줄 사용
  • flake8로 컨벤션 검사 가능 (뭐틀렸는지 알려줌)

    → 요즘은 pep8 의 black 으로 함 (자동으로 고쳐줌)

  • 회사에 처음 들어가면 로직 짜는거보다 사람을 위한 코드를 짜는 것에서 힘들어함




피어 세션

  1. -5~256 숫자는 같은 메모리에 있다고 했는데 이 구조가 해쉬 같은 구조인가?
    해쉬 구조는 아니고 그냥 같은 메모리 보고 있는것비슷하게, 성능을 위해서 빠르게 접근 위함
  2. 리눅스가 다른 운영체제보다 신뢰성과 네트워킹에 유리한 이유는?
    • 원격으로 접근하는데 제약사항이 적다!
    • 윈도우 같은 경우, exe 파일로 전부 설치가 되는데 버전 맞추기가 어렵다. 리눅스의 경우 sudo..apt..등으로 버전 맞추기가 쉽다고 한다.
    • 리눅스 자체가 오픈소스이니까 보안상으로 이슈가 되는 문제점들을 사람들이 빨리 알아내서 수정이 가능하다.
    • 리눅스에서는 읽기와 쓰기와 같은 권한들을 전부 선언할 수 있다.
  3. 파이썬에서 어떤 경우 global 을 선언하는가?
    • 함수 안에 선언 없이 조건문이나 반복문에서 사용될 때
    • 읽기만 할 때는 사실 global 선언 없이 읽을 수 있지만, 값을 바꾸거나 할 때는 global 선언이 없으면 안된다.




과제중 얻은 Tip

문자열

띄어쓰기 처리

  • strip() : 양 옆 띄어쓰기 지우기
  • split() 해주면 strip() 필요없음
  • ” “.join(str.split()) : 문자열을 쪼갰다가 띄어쓰기 하나만 사용하여 붙여줌

특정 문자 삭제

  • replace(‘a’, ‘’) : 스트링의 모든 a 를 공백으로 대체
  • replace(‘a’, ‘’, 1) : 한 번만 위 과정 수행




TA 세션

  • 과제할 때, 컴플렉시티를 고려해서 코딩하자.
    • 코드짤 때마다 스스로 컴플렉시티 계산해야함
  • 리스트 컴프리헨션 쓰면 빨라서 좋다.
    • 하지만 리스트 컴프리헨션이 이중 이상으로 쓰이거나 안에 if-else 가 두 번이상 쓰여서 가독성이 안좋아지면 별로다.
  • 속도와 가독성은 트레이드오프인가
    • 꼭 그렇진 않다.




Today I Felt

관성 장착중

어제보다 많아진 학습량에 (사실 어제는 학습이랄게 없었지..) 놀랐다. 어찌어찌 끝내고 알찬 하루를 보낸 것 같아 기분이 좋다. 아직은 이 생활이 익숙하지 않지만 몇주만 있으면 금방 적응될 것 같다. 관성을 달고 점점 내 역량을 높여가야지.

코드에 관하여…

생각없이 과제를 해버렸다. TA 세션을 하면서 하경 티에이님께서 한 코드를 볼 때마다 컴플렉시티를 묻는 것을 보고 놀랐다. 그리고 내가 짠 코드를 다시 봤고 너무 형편없음을 느꼈다. 분명 더 효율적으로 짤 수 있는데 제출에 급급했기 때문이다. 다음부터는 효율성과 최선의 방법으로 코드를 짜리라 다짐한다.

테스트 케이스

테스트 케이스가 기본이라는걸 느꼈다. 과제를 보니 아무리 쉬운 문제라도 테스트 케이스 없는게 없었다. 주로 적용된 형식은 다음과 같았다.

  • process.py 안에 사용할 여러 함수를 만듦 (메인이 필요하면 name 형태로 만듦)
  • test_process.py 에서 process 와 unittest 를 import 하고 확인할 함수에 대해 다음과 같이 클래스와 함수 형태로 체크하였다.
class TestTextProcessing(unittest.TestCase):
    def test_normalize(self):
        test_str = "This is an example."
        pred = tp.normalize(test_str)
        self.assertEqual(pred, "this is an example.")
  • assertEqual 함수를 사용하면 여러 테스트케이스에 대해 중간에 멈추는 일 없이 결과를 확인할 수 있어 좋았다.

다짐

꾸준히 배울점을 찾고 부족한 부분을 채우자.

가벼운 코드라도 효율성과 최선의 방법을 생각하여 짜자.

테스팅을 생활화하는 프로그래머가 되자.