2월 22, 2022

[CodeKata] 배열에서 과반수가 넘는 숫자

 첫번째 풀이

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
def more_than_half(nums):
    # 아래 코드를 입력해주세요.
    dic = {}     # 숫자와, 그것의 출현횟수 알기 위해 딕셔너리만듬
    for i in set(nums): # 배열 안 숫자들(집합)을 for로 돌려서,
      dic[i] = nums.count(i) 
      # 숫자와(키), 그것의 출현횟수(값) 딕셔너리에 담음
    max_value = max(dic.values()) # 딕셔너리 값들 중 제일 큰 값
    for j in dic.keys(): # 딕셔너리의 키들 중 
      if dic[j] == max_value: # 해당 딕셔너리키가 제일큰값의 키면
        return j  # 리턴해라 조

좀 난잡하게 풀었습니다. set로 nums안 숫자를 추린후, 딕셔너리에 숫자와 그것의 출현횟수를 담고, 딕셔너리의 키 중 가장 큰 value를 가진 키를 리턴해주었습니다.


두번째 풀이 

1
2
3
4
5
def more_than_half(nums):
  dic = {}
  for i in set(nums):
    dic[i] = nums.count(i)
  return [k for k,v in dic.items() if max(dic.values()) == v][0]

풀이 방식은 같은데, 코드가 훨씬 깔끔해졌습니다.

특히, 딕셔너리에서 가장 큰 값을 가진 키를 불러오는 법을 리스트 컴프리헨션 을 가지고 수행했는데, 신선한 접근이었습니다. 

리스트 컴프리헨션을 만들며 고생을 좀 했는데, 왜냐면 k,v in dic 이런식으로 dic.items()가 아닌 딕셔너리 자체를 썼기 때문이다.


dic.itmes()를 프린트해보면 이와 같습니다. 리스트 안에 튜플이 담기는데, 튜플의 앞의 값은 키이고/ 뒤의 값은 1입니다.

만약 이 튜플값을 리턴하고 싶으면, 

def func_1(num1,num2): return num1,num2 result1, result2= func_1(10,20) print(result1) >>10 print(result2) 
>>20 

이런식으로 각각의 변수에 담아 서 각각의 값을 리턴 받을 수도 있다.
2월 16, 2022

[CodeKata] 중복되지 않은 알파벳으로 이루어진 제일 긴 단어의 길이를 반환

 문제는 이러합니다.


풀이 1.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
def get_len_of_str(s):
    # 아래 코드를 작성해주세요.
    lst = []
    if s == "":
        return 0
    else:
        lst.append(s[0])
        longest = []
        for i in s[1:]: 
            if i in lst:
                longest.append(len(lst))
                lst = [i]
            elif i not in lst:
                lst.append(i)
                longest.append(len(lst))
        return max(longest)저

접근 방법:

for 문을 통해 s의 각 요소들을 리스트에 넣어주다가, 중복되는 값이 나올 시, 리스트의 길이를 저장해 준 뒤, 리스트를 초기화하고 중복되는 값부터 새롭게 추가되는 리스트를 추가하려 했습니다.


우선, 요소들을 넣어줄 lst라는 리스트를 만들어줍니다. 

원래는 else: 문 이하의 코드만 만들었는데요, s가 빈 리스트인 경우에는 7번째 줄의 

lst.append(s[0])이 Index out of range 에러가 나게 됩니다. 따라서 if와 else로 s가 빈 문자열인 부분과, 아닌 부분을 분기해주고 시작합니다.

s가 0이 아니라면, lst라는 리스트에, 문자열 s의 첫 번째 값을 넣어줍니다.

여기서 실수를 하나 했습니다.

lst.append([s[0]])


수를 하나 했습니다. 이런식으로 s의 첫번째 값을 리스트 안에 넣어서 lst에 append 해주는 바람에, 나중에 for문에서 lst안에 i 값이 있냐를 따질때 문제가 발생했습니다.
예를들어 [['a']] 면 리스트 안에 ['a']가 있는 것이지 'a'가 있는 것이 아니잖아요? 그래서 고생을 좀 하다가 바깥의 []를 제거해줌으로써 문제를 해결할 수 있었습니다.
또한, 짤리기 전까지의 각 리스트의 길이를 비교해주기 위해 longest란 리스트를 만들었습니다.

다음으로 for문을 돌려주어서 i가 lst안에 있나 / 없나를 확인합니다.
문제에서 "중복되지 않은 알파벳" 이라고 해주었기 때문에, 바로 전의 값이 아니더라도 , 이전의 문자열 어딘가에서 중복되는 값이 있다면, 거기서 lst가 커지는 것을 멈춰줘야 합니다.
따라서 in 을 사용하여, lst 리스트의 어디에라도 i와 같은 값이 있는지를 확인했습니다.

만약 i가 lst안에 있다면, longest에 여태까지의 lst의 length값을 넣어주고, lst를 i값으로 초기화해줍니다.
만약에 i가 lst안에 없다면, lst에 i값을 더해주는 것을 계속합니다. 이때도 longest에 lst의 length 값을 계속 append 해줘도 되는데, 어차피 max의 값을 꺼낼것이기에 상관없습니다.

그리고 longest의 max값을 리턴해주면 됩니다.
2월 15, 2022

[CodeKata] reverse 함수

 

문제 


풀이 1.

1
2
3
4
5
6
7
8
def reverse(number):
  # 여기에 코드를 작성해주세요.
  if int(number) > 0:
    numbers = str(number)[::-1]
    return int(numbers)
  elif int(number) <= 0:
    numbers2 = str(-1*number)[::-1]
    return int(numbers2) * -1

2월 11, 2022

Find longest word

 



풀이 1.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def find_longest_word(words):
    # 아래 코드를 작성해주세요.
  a =""
  long =0
  for word in words:
    if len(word) > long:
      long = len(word)
      a = word
    else:
      pass
  return a

풀면서 겪은 문제점

11라인의 return a를 for 문 안에 사용해줘서, 각각의 for loop 이 돌때마다 a가 리턴됐습니다.  a의 들여쓰기를 취소하여 for문 밖으로 빼줌으로써 문제를 해결했습니다.


다른 풀이

1
2
3
4
5
6
7
def find_longest_word(words):
    # 아래 코드를 작성해주세요.
  m = ""
  for i in words:
    if len(i) > len(m):
      m = i
  return m

위에서처럼 길이(long)과 가장 긴 문자(a)를 굳이 구분하지 않았습니다. 그냥 매 for loop을 돌면서 m이라는 변수에 더 긴 문자가 있다면 교체해주고, for문을 모두 돈 결과를 반환했습니다. 더 깔끔한 코드입니다.


1월 30, 2022

TIL - Class

 객체지향 프로그래밍

: 시스템을 컴퓨터 명령어의 목록으로 구성하지 않고, 여러 다양한 객체들의 집합으로 구성하는 것.

= 프로그램을 객체(object)라는 기본 단위로 나누고, 객체들의 상호작용으로 기능을 구현하는 방식


클래스 없이 코딩을 하게 되면, 데이터함수가 따로 분리돼 정의되므로 문제가 생깁니다.

함수의 사용자가 함수의 작동 로직에 대해 이해도가 부족하다면, 어떠한 데이터와 필드를 넘겨줘야 하는지 이해하지 못했기에 함수를 사용하는데 있어 불편이 커지고 잘못된 매개변수를 넘길 수 있기에 오류의 가능성도 높아집니다. 


따라서 클래스를 사용합니다

Class의 정의: Class = Data + Functions


일단 클래스의 객체가 생성되고 나면, 그 후에는 함수의 코드가 어떻게 구현됐는지 몰라도 함수를 사용하는데 아무런 문제가 없습니다. 

더불어서, 클래스를 정의함으로써 새로운 type이 생기게 됩니다.

다시 말해, 클래스는 (자동차, 사용자와 같은) 어떠한 개념/컨셉/실체를 나타내는 데이터해당 데이터를 기반으로 실행시킬 수 있는 함수들을 분리하지 않고 하나로 묶어서 해당 개념을 표현하는 나만의 새로운 데이터 타입을 만들 수 있게 하여 프로그래밍을 훨씬 효과적으로 할 수 있도록 해줍니다.

1월 29, 2022

import는 어떻게 module과 package를 찾는가

1. sys.module과 sys.path 의 차이점

파이썬에서 모듈이나 패키지를 사용하기 위해선 해당 파일(모듈의 경우) 혹은 디렉토리(패키지의 경우) 가 어딨는지 파이썬이 찾을 수 있어야 import를 하여 사용할 수 있습니다. 이때 파이썬 정해진 순서에 따라 모듈 or 패키지를 찾습니다.

  1. sys.modules
  2. built-in modules
  3. sys.path
먼저, 2번의 built-in modules의 경우, 말 그대로 파이썬에서 제공하는 공식 라이브러리들을 의미합니다. 이미 파이썬에 포함돼 나오므로 파이썬이 쉽게 찾을 수 있습니다.

1번의 sys.modules는 파이썬이 가장 먼저 확인하는 곳 입니다.
sys.moudles는 이미 로드돼 있는 모듈에 이름을 mapping하는 dictionary로서, 이미 import 된 모듈과 package들을 저장하고 있습니다. 즉, sys.modules덕분에 한번 import한 모듈이나 package를 파이썬이 또 다시 찾을 필요가 없습니다. 반대로 말하자면, 새로 import 하는 모듈이나 패키지는 sys.modules에서 찾을 수 없다는 뜻입니다.
sys.modules는 강제로 다시 로드할 수 있도록 조작할 수 있습니다.
또한, Dictionary 형임에도 불구하고, 내부의 요소들을 마음대로 수정할 수는 없고, 필수적인 부분을 건들 경우 파이썬 전체에 문제가 생길 수도 있습니다. 


3번의 sys.path는 파이썬이 가장 마지막으로 보는 장소입니다.
sys.path는 기본적으로 list이며, string 요소들을 가지고 있는 list입니다.

각 string 요소들은 이처럼 특정 경로를 나타냅니다. 
파이썬은 이 list의 경로들을 하나하나 확인하면서 해당 경로에 import 하고자 하는 package혹은 모듈이 위치해 있는지 확인합니다. 

2. sys도 import 해야되는 모듈입니다. 파이썬은 sys 모듈의 위치를 어떻게 찾을 수 있을까요?

1
2
import  sys
print(sys.modules)

{'sys': <module 'sys' (built-in)>, 'builtins': <module 'builtins' (built-in)>
sys를 import하여 sys.modules를 print해보면 이를 알 수 있습니다. 
sys module은 built-in 돼있기에, built-in module들이 있는 부분에서 찾게 됩니다.


3. Absolute path와 relative path 차이점을 서술

  • 파이썬의 built-in 모듈과 pip를 통해 설치한 외부 모듈 및 패키지는 일반적으로 import 하는데 큰 문제가 되지 않습니다.

    Built-in 모듈은 당연히 잘 찾아지고, pip로 설치한 외부 모듈도 자동으로 site-packages 라는 디렉토리에 설치가 되는데, 이 site-packages는 sys.path에 이미 포함돼 있기때문에 찾는데 문제가 없습니다.

    문제는 직접 개발한 local package입니다. 직접 개발한 local package를 import 할때는 해당 package의 위치에 맞게 import경로를 잘 선언해야 합니다

    Local package를 import 하는 경로에는

    1. absolute path
    2. relative path

    가 있습니다

    • absolute path

      1. 이름 그대로 절대 경로입니다
      2. import를 하는 파일이나 경로에 상관없이 항상 경로가 동일하기 때문

      예를들어,



      my_app이라는 프로젝트이며 package1과 package2 라는 2개의 package를 가지고 있습니다

      그리고 package2는 subpackage1라는 중첩 package를 가지고 있습니다

      Absolute path를 사용해 package1 과 package2를 import 하면 다음과 같습니다

      from package1 import module1
      from package1.module2 import function1
      from package2 import class1
      from package2.subpackage1.module5 import function2
      

      경로들의 시작점이 전부 my_app 프로젝트의 가장 최상위 디렉토리에서 시작하는 것을 볼 수 있습니다

      예를들어, subpackage1의 module5 모듈의 function2 함수를 import하려 거치는 경로는 아래와 같고,

      my_app => package2 => subpackage1 => module5.py

      이를 윈도우의 directory 형식으로 바꾸면

      my_app\package2\subpackage1\module5.py

      파이썬에서는 slash나 backslash(\)대신에 dot(.)을 사용해서 경로를 표현합니다

      이미 my_app 프로젝트 안에 있으므로 my_app은 생략하고 경로를 표현하게 됩니다.

      package2.subpackage1.module5.py

      이걸 from import키워드를 사용해 import 하려면 어떻게 할까요??

      from package2.subpackage1.module5 import function2

      정리하자면, my_app 프로젝트 내에서는 어느 파일, 어느 위치에서 import 하던지 경로가 항상 위와 동일하게 되므로 absolute path라고 하는 것입니다

      참고로 current directory 즉 현재의 프로젝트 디렉토리는 default로 sys.path에 포함합니다.

      그러므로 absolute path 는 current directory부터 경로를 시작하게 되는 것입니다.

      일반적으로 local pacakge를 import 할 때는 absolute path를 사용하면 됩니다.

      그러나 경로가 길어질 수 있다는 단점이 있습니다.

    • relative path

      경로가 길어지는 absolute path 의 단점을 보완하기 위해서 relative path를 사용합니다.

      Relative path는 프로젝트의 최상단 디렉토리를 기준으로 경로를 잡는게 아니라. import하는 위치를 기준으로 경로를 정의합니다.

      그래서 relative path는 local package안에서 다른 local package를 import 할 때 사용됩니다



      예를들어 package2의 module3에서 package2의 class1과 package2의 하위 package인 subpackage1의 module5의 function2 함수를 import하려고 하면??

      # package2/module3
      
      from . import class1
      from .subpackage1.module5 import function2
      

      여기서 dot . 은 import가 선언되는 파일의 현재 위치를 이야기 합니다

      현재 위치는 package2/module3.py 이므로, 현재 위치에서부터 원하는 모듈의 경로만 선언해주면 되는 것입니다

      또한 dot2개를 사용할 수도 있습니다. dot 2개 .. 는 현재위치에서 상위 디렉토리로 가는 경로입니다

      # subpackage1/module5.py
      from ..module4 import class4
      

      Relative path는 선언해야 하는 경로의 길이를 줄여준다는 장점 은 있지만

      헷갈리기 쉽고 파일 위치가 변경되면 경로 위치도 변경되어야 하는 단점 이 있기에

      웬만한 경우 absolute path를 사용하는 것이 권장됩니다

 

1월 27, 2022

집단자료형: Set,Dictionary, List,Tuple

1. Set과 Dictionary의 차이

    a. set은 요소들이 순서 없이 존재하므로 몇 번째 요소를 읽어들이거나 할 수 없습니다. 

        반대로 Dicitonary는 요소들을 key 값을 통하여 읽어들일 수 있습니다(key값 중복X)

    b. dictionary의 key값은 변경할 수 없는 형식임에 반해, value값은 mutable(변경가능)합니다

        반대로 set은 mutable한 리스트형 입니다

2. List와 Tuple의 차이   

    a. 가변형인 리스트와 달리 튜플은 한번 선언된 이후 수정이 불가능 합니다

    b. list는 수정이 가능하고 여러 수의 요소들을 저장할 수 있도록 했기때문에 tuple보다 차지          하는 메모리 용량이 더 큽니다. 이에 반해 tuple은 제한적인 만큼 용량이 적습니다. 

     따라서 수정이 필요없고, 간단한 데이터를 표현할 때는 tuple을 사용하는것이 효과적입니다

1월 26, 2022

Basic Grammar1.

 파이썬 기초 문법 학습 과정에서 개인적으로 헷갈리거나 어려웠던 내용들을 정리했습니다.

1. Data types

 : 해당 데이터의 타입을 이야기 합니다. 2 / "2" / 2.0 은 사람에게는 문맥상 같은 의미를 가지지만, 컴퓨터에는 해당 3개가 모두 다른 의미를 가집니다. 그렇기에 정확한 data type의 값을 지정하는 것이 중요합니다. 

  1. String : "" 안에 있는 글자들
  2. Integer : 정수
  3. Float : 소수점 숫자
  4. Complex Numbers
  5. Boolean

 파이썬에는 다음과 같은 데이터 타입들이 존재합니다.

 4번 complex Numbers를 볼까요? 생소한 표현입니다. 이는 실수와 허수를 포함하고 있는     "복소수" 를 이야기합니다. 파이썬에서는 j를 이용하여 허수를 표현합니다.

ex) 1 + 3j, 2-4j

5번 Boolean은 True 와 Fasle, 즉 참과 거짓을 나타내는 데이터타입 입니다.

"True" 혹은 "False"처럼 큰따옴표 "" 로 묶어주면 string으로 잡히게 됩니다. true나 false처럼 대문자를 마음대로 변경해서도 안됩니다. 정확히 True와 False로 적어주셔야 합니다.


2. 변수 이름 법칙 

변수는 데이터의 식별자(일종의 별명)의 역할을 합니다. 이러한 변수의 이름을 정하는 것에도 법칙이 있습니다.

  • 변수이름은 영어 알파벳/ 숫자 / _(underscore)으로만 구성해야 합니다
  • 변수이름의 첫 글자는 알파벳이나 _(underscore)으로만 시작해야 합니다
  • 숫자로 시작할 수 없습니다
  • 영어 알파벳은 대,소문자가 구분이 됩니다
특히 저는 언더스코어_ 대신 - 를 사용한다거나, 변수의 중간에 공백(스페이스)를 넣는다거나 하는 실수를 자주 하는데요, 이러한 실수를 하지 않도록 주의해야 합니다.

3. Math Expressions

num1 = 10
num1 = num1 + 1

은 

num += 1 

로 간단하게 풀어쓸 수 있습니다. 

이러한 표현 방식은 +에만 해당되는 것이 아닙니다. 

-=

*= 

/=

%= 

등, 변수 이름을 두번 반복하는 번거로움을 피하기 위해서 다양한 연산방식을 짧게 ()= 식으로 사용할 수 있습니다.

4. Concatenating Text Strings

string을 더하는 방법엔 두가지가 있습니다

1. + 를 사용하기

print("Hello, " + "World") 

결과는 "Hello, World" 가 될 것입니다.

+는 특정 문자열이 변수에 저장돼 있을 때 사용하면 편리합니다. 

1
2
3
name = input()

print("Hello, " + name)

이런식으로 문자열이 들어있는 변수를 string에 이어줄 수 있습니다.

2. literal string interpolation 

a. 따옴표 앞에 f 를 붙여줍니다

b. 치환하고 싶은 변수(변수가 아니어도 됩니다, 함수 호출 등 가능) 를 {} 를 사용해서 표시합 니다

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
date            = 1980
python_inventor = "Guido van Rossum"
location        = "Centrum Wiskunde & Informatica"
country         = "Netherlands"

print(f"""Python was conceived in the late {date}s 
by {python_inventor} at {location} (CWI) in the {country} as a successor 
to the ABC language (itself inspired by SETL), capable of exception handling 
and interfacing with the Amoeba operating system. 
Its implementation began in December 1989.""")

이렇듯 길고 복잡한 문자열의 경우에 literal string interpolation을 사용해주면 효과적입니다.


1월 11, 2022

23.7 지뢰찾기 문 (코딩도장)

 




이 문제를 두고 이틀 정도 고민을 했습니다.

저는 재래식 문과라 무식하게 모든 경우의 수를 나눠서 모든 경우의 수의 경우의 수에 if문의 if문을 다는 식으로 접근을 했습니다

이렇게요. 
이렇게 일일이 접근하다 보니, 매번 코드를 작성하다가 내가 어디까지 왔는지 까먹어 버렸고, 매번 쓰다 지우다를 반복했습니다. 한 네번 시도해봤는데.. 사진만큼 적고나면 정신이 복잡해져서 저기서 나아갈 수가 없었습니다.

그래서 답을 봤습니다! (...?)
답을 보니 역시, 무식하게 접근하지 않는 방법이 있더군요. 


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
col,row = map(int,input().split()) #col이 가로 row가 세로

matrix = []
for i in range(row):  #세로 범위의 i에서
     matrix.append(list(input())) #matrix리스트에 입력받은 리스트를 넣어라
     #input으로 입력받은 것을 리스트로 변환하고, 그것을 matrix에 append하면 2차원 리스트가 됨


for i in range(row):
  for j in range(col):
    cnt = 0
    if matrix[i][j] == ".":       # .일 때
      for y in range(i-1,i+2):    # matrix[i][j]의 위 아래 한칸씩 범위안의
        for x in range(j-1,j+2):    #matrix[i][j]의 오른쪽,왼쪽 한칸씩 범위에서 (한마디로 둘러싼 주변에서)
          if not(y<0 or y>=row or x<0 or x>= col): #만약 이 둘러싼 x,y들이 본래 있어야 할 범위를 벗어나지 않을때
            if matrix[y][x] == "*": # 그것들이 *이라면
              cnt += 1              # cnt에 1더해줘라
      matrix[i][j] = cnt          # matrix[i][j]는 cnt할당
      print(matrix[i][j], end ="")  # 바뀐 값을 출력하는데, 줄 바뀌지 않도록 end="" 지정
    else:
      print(matrix[i][j], end ="")   # 바뀌지 않은 원래 값을 줄 바뀌지 않도록 출력
  print()                           #i안의 j 한바퀴 돌린 후마다 print()로 한줄 띄어줌

저 정신 없는 주석들은 제가 단 것입니다.. 나름 알기 쉽게 설명한다고 써봤습니다만..

제가 이해한 것을 간단하게 요약하자면

1. 2차원 리스트를 만들고

2. for range로 col, row만큼 반복하면서

3. 요소가 *인 경우 주변을 검사합니다

4. 주변 검사는 range(i-1,i+2), range(j-1,j+2)을 통해 주변 요소 8개를 탐색하면서,

5. 만약 주변요소가 (y<0 or y>=row or x<0 or x>= col) 로 리스트의 범위를 벗어난다면 그 경우를 빼고,

6. 주변요소가 *이라면 cnt에 1씩 더해주는 것입니다

7. 그리고 더해진 cnt를 값으로 바꿔준 이후 형식에 맞게 출력하면 됩니다.


코드를 보니 간단하더군요.. 주변 범위를 지정해서 예외를 제외하고 검사하는 깔끔한 방식이어서 보기도 좋습니다.

제 스스로 만들지 못해서 아쉽습니다. 다음번엔 꼭 스스로 해보죠..