TIL-no.28 Python advanced 6

sanghun Lee·2020년 7월 13일
0

Today I Learned

목록 보기
26/66

파이썬으로 파일을 생성하고 문자열 넣기

file = open('hello.txt','w') #hello.txt파일을 쓰기모드로(w)열기,w는 write의 쓰다이다.
file.write('Hello, world!') #파일객체를 얻었으니 내용바꿔줌
file.close() #파일객체 닫기
  • 문자열 읽기
file = open('hello.txt','r') #hello.txt파일을 읽기모드(read)로 열기. 파일객체 반환
s=file.read() #파일에서 문자열 읽기
print(s) #Hello,world!
file.close() #파일객체닫기
  • 매번 close로 닫으면 귀찮으니 with as 사용하여 자동으로 파일객체 닫기// with as 사용하면 파일을 사용한뒤 자동으로 파일객체를 닫아준다.
with open('hello.txt', 'r')as file: #hello.txt파일을 읽기모드(r)로 열기
    s = file.read() # 파일에서 문자열 읽기
    print(s) # Hello,world!
  • 문자열 여러줄을 파일에 넣기
with open('hello.txt', 'w')as file:
    for i in range(3):
        file.write("Hello, world! {0}\n".format(i))
  • 리스트에 들어있는 문자열을 파일에 써넣기
lines = ['안녕하세요.\n', '파이썬\n', '코딩도장입니다.\n']

with open('hello.txt', 'w')as file:
    file.writelines(lines)
  • 파일의 내용을 한줄씩 리스트로 가져오기
    유의해야할 점은 read는 내용을 읽어 문자열로 가져오지만 readlines는 파일의 내용을 한줄 씩 리스트 형태로 가져온다)
with open('hello.txt','r')as file:
    lines = file.readlines()
    print(line)
  • 파일의 내용을 한줄 씩 읽기
    Readline에서는 while을 쓰는것이 좋은데 파일에 문자열이 몇줄이나 있는지 모르기 때문이다.
with open('hello.txt', 'r')as file:
    line = None  #line != ''와 충돌하지 않게 하기위해 None으로 미리 변수선언
    while line != '': #빈문자열이면 멈춤 문자열이 존재하면 계속 반복
        line = file.readline() #문자열 한줄씩 읽어서 변수에 저장
        print(line.strip('\n'))
  • for 반복문으로 파일의 내용을 줄단위로 읽기
with open('hello.txt', 'r')as file: #hello.txt파일을 읽기모드(r)로 열기
    for line in file: #for에 파일객체를 지정하면 파일의 내용을 한줄씩 읽어서 변수에 저장함
        print(line.strip('\n')) #파일에서 읽어온 문자열에서 \n 삭제하여 출력

파일객체는 이터레이터라 변수여러개에 저장하는 언패킹도 가능하다

file = open('hello.txt', 'r')
a,b,c =  file
print(a,b,c) #이때 할당변수의 개수와 파일에 저장된 문자열의 줄 수가 일치해야한다.

pickle module

파이썬은 객체를 파일에 저장하는 pickle 모듈을 제공한다.
객체를 파일에 저장하는것을 pickling, 파일에서 객체를 읽어오는 과정을 unpickling이라고 한다.

import pickle

name = 'james'
age = 18
address = '서울시 용산구 한남동'
scores = {'korean': 90, 'english': 95, 'mathematics': 85, 'science': 82}

with open('james.p', 'wb')as file: #james.p 파일을 바이너리쓰기모드(writebinary)로 열기
    pickle.dump(name, file)
    pickle.dump(age, file)
    pickle.dump(address, file)
    pickle.dump(scores, file)
  • unpickling하기
import pickle

with open('james.p', 'rb')as file:
    name = pickle.load(file)
    age = pickle.load(file)
    address = pickle.load(file)
    scores  = pickle.load(file)
    print(name)
    print(age)
    print(address)
    print(scores)
  • 파일에서 10자 이하인 단어 개수세기
#1.문자열파일만들기
words = ['anonymously\n',
        'compatibility\n',
        'dashboard\n',
        'experience\n',
        'photography\n',
        'spotlight\n',
        'warehouse\n']

with open('words.txt', 'w')as file:
    file.writelines(words)


#2 단어개수세기
with open('words.txt', 'r')as file:
    count = 0
    words = file.readlines()
    for word in words:
        if len(word.strip('\n')) <= 10:
            count += 1
    print(count)
  • 공백으로 문자열 분리 뒤 c가 존재하는 단어만 가져오기
with open('words.txt', 'r')as file:
    words = file.readlines()

    for word in words: #
        word2 = word.split() #
    
    for i in word2: 
        if 'c' in i: #
            print(i.strip(',.'))

문자열을 응용하여 회문판별 및 N-gram만드는방법

회문은 거꾸로 읽어도 제대로 읽은것과 같은문장임(level, SOS), 즉 가운데 문자를 기준으로 왼쪽 오른쪽 문자가 같음. 회문(palindrome)은 유전자 염기서열 분석, N-gram은 빅데이터 분석, 검색엔진에서 많이 쓰인다.

word = input('단어를 입력하세요')

is_palindrome = True #회문판별값을 저장할 변수, 초기값은 True
for i in range(len(word) // 2): #0부터 문자열 절반만큼 반복
    if word[i] != word[-1-i]: #왼쪽문자와 오른쪽 문자를 비교하여 문자가다르면 회문이 아님
        is_palindrome = False
        break
print(is_palindrome)
#판별시 문자열길이가 중요한데 홀수 길이면 가운데 글자 바로 앞까지만 검사하게됨.

시퀀스 객체의 슬라이스를 활용하여 간단하게 판별할 수가 있다.

word = input('단어를입력하세요:')
print(word == word[::-1])

원래의 문자열과 반대로 뒤집은 문자열을 비교하며 전체에서 인덱스 1씩 감소시키면서 요소를 가져오므로 반대로 뒤집은것과 같은 것이다.

반복가능한 객체의 요소순서를 반대로 뒤집는 Reversed를 사용해도된다.

list에 문자열을 넣으면 하나하나가 리스트의 요소로 들어가므로 비교가 가능하다.

word = 'level'
list(word) == list(reversed(word))

#join, reversed 메소드 사용하기
word = 'level'
word == ''.join(reversed(word))

#join은 구분자 문자열과 문자열 리스트의 요소를 연결한다. 여기서 빈문자열''에 reversedword의 요소를 연결했으므로 문자순서가 반대로된 문자열을 얻을 수 있다

N-gram

문자열에서 N개의 연속된 요소를 추출하는 방법이다.

  • 2-grma씩 출력하기
text = 'Hello'

for i in range(len(text)-1): #2gram이므로 홀수이니까 문자열 끝에서 한글자 앞까지만 반복
    print(text[i], text[i+1], sep = '') #현재문자와그다음문자 출력
for i in range(len(text)-2): #3gram
    print(text[i], text[i+1],text[i+2], sep='')
  • 단어단위 n-gram
text = 'this is python script'
words = text.split() #공백을 기준으로 문자열을 분리하여 리스트로 만듦

for i in range(len(words)-1):
    print(words[i], words[i+1])
  • zip을 이용한 ngram

zip은 보통 리스트 두개를 딕셔너리로 만들때 사용했는데 위와 같이 사용하면 한개씩 밀린상태에서 각 문자를 묶은 튜플이 생성됨.
문자를 실행한것

text = 'hello'

two_gram = zip(text, text[1:])
for i in two_gram:
    print(i[0], i[1], sep='')

문자열을 실행한것

text = "this is python script"
words = text.split()
print(list(zip(words,words[1:])))

zip에 일일히 text[0:], text[1:]등을 넣었던것을 아래와 같이 반복문으로 바꿔서 처리할 수 있다.

text = 'hello'
print([text[i:] for i in range(3)])
  • zip은 반복가능한 객체 여러개를 콤마로 구분해서 넣어줘야 한다.
a= list(zip(['hello', 'ello', 'llo']))
print(a)
#이렇게하면  요소가 3개들어있는 list1개를 넣어준것이기 때문에  3gram으로 출력되는 것이안됨.
b=list(zip(*['hello','ello', 'llo']))
print(b) 
  • 리스트 언패킹(list unpacking)
    zip에 각 요소를 콤마로 구분해서 넣어주려면 리스트앞에 을 붙여야한다.
    이렇게 list 에
    을 붙이는 방법을 리스트언패킹이라고 한다.
text = "hello"
c= list(zip(*[text[i:]for i in range(3)]))
print(c)
  • N-gram 예제
  1. 예제1
    입력된 숫자에 해당하는 단어단위 N-gram을 튜플로 출력해보자.
    ,만약 입력된 문자열의 단어개수가 입력된 정수 미만이라면 worng을 출력한다
    여기서 문자열 리스트는*[words[i:]for i in range(n)]을 통해 n 만큼 반복하여 리스트로 만들어준다.
n = int(input())
text = input()
words = text.split() #입력된 한줄로 된 문자열 값은 split을 사용하여 분할한 뒤 words 변수에 저장

if (len(words) < n): #입력된 숫자n 보다 단어갯수가 적으면 wrong 출력
    print('wrong')
else: 
    n_gram = zip(*[words[i:]for i in range(n)])
    for i in n_gram:
        print(i)
  1. 예제2
    words.txt 파일에서 회문인 단어만 다시 가지고 나오는 코드
with open('words.txt','r')as file:
    words = file.readlines()
    for word in words:
        word = word.strip('\n')
        if word == word[::-1]:
            print(word)

함수(function)

함수에서 doc string 사용하기 doc string윗줄에 다른코드가 오면 안된다.

def add(a,b):
    """"""이 함수는 a,b를 더한 뒤 결과를 반환하는 함수 입니다 .""""""
    return a + b

x = add(10,20)
print(x)
  • 함수의 독스트링을 출력하는 방법
    print(add.__doc__)

  • help(add)

help 에 함수이름을 넣으면 이름,매개변수,독스트링을 도움말로 출력해준다.

  • 매개변수가 없고 반환값만 있는 함수
def one():
    return 1
x = one()
print(x)
  • return
    함수 중간에서 빠져나오기 위해 사용되기도 한다.
    이렇게 하면 a 가 10일때 None으로 출력된다.
def not_ten(a):
    if a == 10:
        return 
    print(a, '입니다', sep = '')

print(not_ten(5))
print(not_ten(10))

함수 호출(스택다이어그램으로 알아보기)

스택은 접시쌓기와 같이 접시를 차곡차곡쌓고 꺼낼때 위쪽부터 차례대로 꺼내는 방식이다.

파이썬에서는 접시쌓기와 방향이 반대인데, 함수가 아래쪽 방향으로 추가되고 함수가 끝나면 위쪽방향으로 사라진다.

def mul(a, b):
    c = a * b
    return c

def add(a, b):
    c = a + b
    print(c)
    d = mul(a,b)
    print(d)

x = 10
y = 20
add(x,y)
  • 몫과 나머지를 출력하는 함수 만들기
x = 10
y = 3

def get_quotient_reaminder(a,b):
    return a//b , a%b

qoutient, reaminder = get_quotient_reaminder(x,y)
print('몫: {0}, 나머지:{1}'.format(qoutient, reaminder))
  • 사칙연산 반환 함수
x,y = map(int, input().split())
def calc(a,b):
    return a+b, a-b, a*b, float(a/b)

a,s,m,d = calc(x,y)
print('덧셈:{0}, 뺄셈:{1},곱셈:{2},나눗셈:{3}'.format(a,s,m,d))

인수(positional argument)

함수에 인수를 순서대로 넣는 방법을 인수(positional argument)라고 합니다. 인수의 위치가 정해져있는것.

def print_numbers(a,b,c):
    print(a)
    print(b)
    print(c)
print_numbers(10,20,30) #이렇게하면 각줄에 숫자하나  씩 출력된다
  • 언패킹(unpacking)
x = [10,20,30]
print_numbers(*x) 

''을 붙여언패킹 은 함수(리스트), 함수([애스터리스크]튜플)등으로 사용가능하
며 위와 같은 기능을 보여준다.
위치 인수와 리스트 언패킹은 인수의 개수가 정해지지 않은 가변인수에 사용한다. 같은 함수에 인수한개를 넣을수도 열개를 넣을수도 안 넣을 수도 있으니까
가변인수는 아래와 같이 매개변수앞에 애스터리스크(*)를 붙여 만든다.
여기서 args는 튜플이라 for문으로 반복가능하다.

def print_numbers(*args):
    for arg in args:
        print(arg)

print_numbers(10)
print_numbers(10,20,30,40)
x = [10]
y = [10,20,30,40]
print_numbers(*x)
print_numbers(*y)

이렇게 함수를 만들때 매개변수에 를 붙여주면 가변인수함수를 만들 수 있다.그리고 이런 함수를 호출 할때는 인수를 각 각 넣거나 리스트(튜플)의 언패킹()을 사용하면된다.

고정인수와 가변인수 함께 사용하기

def print_numbers(a, *args):
    print(a)
    print(args)

print_numbers(1)
print_numbers(1, 10,20)
print_numbers(*[1,10,20])
#a 에는 하나가 배정되고 나머지는 알아서 가변인수로 넘어감

keyword argument

일단 왜 필요한지 예시를 보자

def personal_info(name, age, address):
    print('이름: ', name)
    print('나이: ', age)
    print('주소: ', address)

이와 같이 함수를 사용할때 만약 인수의 순서에 대한 정보를 알지 못하면 이상한 출력값이 나올 수도 있다.

그것을 방지하기 위해서 이 기능을 제공한다. 말그대로 인수에 키워드를 붙일 수 있음
순서를 지키지 않아도 원하는순서에 알맞게 출력할 수 있음.

personal_info('홍길동', '서울시 용산구 이촌동', 30)
personal_info(name = '홍길동',  address = '서울시 용산구 이촌동', age = 30) 

이렇게 딕셔너리에 ** 을 붙여 함수에 넣어주면 (키워드가 반드시 문자열 형태일때) 딕셔너리에 저장된 값을 출력할 수 있음.

x = {'name':'홍길동', 'age':30,'address':'서울시 용산구 이촌동'}
personal_info(**x)

딕셔너리의 키이름과 함수의 매개변수 이름이 같을 때 사용가능. **두번쓰는 이유는 딕셔너리는 키와 발류가 있
기에 그럼 한번만 쓰면 키값만 나온당

  • 키워드 인수를 사용하는 가변 인수함수 만들기
def personal_info(**kwargs):
    for kw, arg in kwargs.items():
        print(kw, ':', arg, sep = '')
personal_info(name = '홍길동', age = 30, address = '서울시 용산구 이촌동')
   
x = {'name': '홍길동'}
personal_info(**x)
y = {'name':'홍길동', 'age':30, 'address':'서울시 용산구 이촌동'}
personal_info(**y)

보통 **kwargs 를 사용한 가변인수 함수는 다음과 같이 함수안에서 특정키가 있는지 확인한 뒤 해당기능을 사용한다.

def personal_info(**kwargs):
    if 'name' in kwargs: #in을 사용하여 딕셔너리안에 특정키가 있는 지 확인
        print('이름:', kwargs['name'])
    if 'age' in kwargs:
        print('나이:', kwargs['age'])
    if 'address' in kwargs:
        print('주소:', kwargs['address'])
  • 매개변수에 초기값 미리 지정하기 (초기값이 지정된 변수는 제일 뒤에 둘 것.)
def personal_info(name, age, address = '비공개'):
    print('이름: ', name)
    print('나이: ', age)
    print('주소: ', address)
personal_info('홍길동', 30) 
personal_info('홍길동', 30, '서울시 용산구 이촌동')
  • kwargs 예제

표준입력으로 국어,영어,수학,과학점수가 입력되고 가장높은점수,가장낮은점수,평균점수(실수)가 출력되게 만들자

korean, english , mathematics, science =map(int, input().split())

def get_min_max_score(*args):
        return min(args), max(args) 

def get_average(**kwargs):
    return sum(kwargs.values())/len(kwargs)

min_score, max_score = get_min_max_score(korean, english, mathematics, science)
average_score = get_average(korean=korean, english=english,
                            mathematics=mathematics, science=science)
print('낮은 점수: {0:.2f}, 높은 점수: {1:.2f}, 평균 점수: {2:.2f}'
      .format(min_score, max_score, average_score))
 
min_score, max_score = get_min_max_score(english, science)
average_score = get_average(english=english, science=science)
print('낮은 점수: {0:.2f}, 높은 점수: {1:.2f}, 평균 점수: {2:.2f}'
      .format(min_score, max_score, average_score))

재귀호출(Recursive call)

함수안에서 자기 자신을 호출하는 방식을 재귀호출(Recursive call)이라고 한다.
재귀호출은 잘 사용하지 않지만 알고리즘을 구현할 때 매우 유용하다

일단 왜쓰는지 아래를 보며 생각해보자

#이렇게 하면 error 가 발생하며 1000번이 최대인 재귀를 넘어서고 hello()에 의해 계속 깊어지기 때문
def hello():
    print('Hello, world!')
    hello()

hello()

이래서 재귀호출은 종료 조건이 필요하다

#재귀호출 종료조건 만들기
def hello(count):
    if count == 0:
        return
    
    print('Hello, world!', count)

    count -= 1
    
hello(5)

hello(count)# 5로 들어온값에 -1 이 된 4로 다시 hello 함수를 부르고 이와같은 과정을 0이 될때 까지 반복

  • 재귀호출로 팩토리얼 구하기
def factorial(n):
    if n == 1: # n 이 1 일때 1을 반환하고 끝냄
        return 1
    return n * factorial(n-1) #n 과 팩토리얼함수에 n-1을 넣어서 반환된 값을 곱함.

print(factorial(5))

람다 표현식 사용하기

def 표현식이 아닌 람다 표현식으로 익명의 함수를 만드는 방법을 알아보자.
식표현이 간단해 주로 다른함수의 인수로 넣을때 사용된다.
람다표현식은 이름이 없는 함수를 만든다. 그래서 변수 할당을 해줘야한다.

def plus_ten(x):
    return x + 10

print(plus_ten(1))

plus_ten2 = lambda x: x + 10 
print(plus_ten2(1))
  • 람다 표현식 자체를 한번에 호출해버리기
print((lambda x: x+10)(1)) #람다표현식 안에는 변수를 만들 수 없으니 유의해야한다. 변수를 만들필요가 있는 함수는 def를 쓰자
y = 10 #이렇게 밖에 변수를 설정해서 사용 가능하다.
print((lambda x: x+y)(1))
  • lambda 표현식을 사용하는 대표적인 예가 Map이다
def plus_ten(x):
    return x + 10
  • 람다표현식에서 조건부 표현식 사용하기
a = [1,2,3,4,5,6,7,8,9,10]
print(list(map(lambda x: str(x) if x % 3 ==0 else x, a)))
#3의 배수를 문자열로 바꿈 여기서는 ':' 를 조건문안에 붙이지 않는 것에 유념하자
#lambda식에서는 if 를쓰면 무조건 else도 같이 써야한다.
  • filter 사용하기: filter는 반복가능 객체에서 특정조건에 맞는 요소만 가져온다 반환값이 true일때만 가져온다.
def f(x):
    return x > 5 and x < 10

a = [8,3,2,10,15,7,1,9,0,11]
print(list(filter(f,a))) #함수 f 에 참인 요소만 가져온다
#lambda식으로 표현해보기
a = [8,3,2,10,15,7,1,9,0,11]
print(list(filter(lambda x: x>5 and x<10, a)))
  • reduce 사용하기
from functools import reduce
def f(x,y):
    return x + y

a = [1,2,3,4,5]
from functools import reduce
print(reduce(f,a))
#reduce는 누적값을 반환해줌, 3+3+4+5

#lambda식으로는
a =[1,2,3,4,5]
from functools import reduce
reduce(lambda x,y: x+y,a)
#reduce는 반복문으로도 표현가능, 이제 삭제되서 필요없으니까 반복문 익히자
a =[1,2,3,4,5]
x = a[0]
for i in range(len(a)-1):
    x = x + a[i+1]

print(x)

파일이름중 확장자가 .jpg, .png인것만 출력되는 lambda식 만들기

files = ['font', '1.png', '10.jpg', '11.gif', '2.jpg', '3.png', 'table.xslx', 'spec.docx']

print(list(filter(lambda x: x.find('jpg') != -1 or x.find('.png') != -1, files)))
#filter 와 find의 사용법 유의하여 보기 꼭 익히기(크롤링에 필수 임)
"""
files = input().split()

람다식을 이용하여 input 되는 파일명 리스트를 일관성있는형식으로 출력하기

입력

1.jpg 10.png 11.png 2.jpg 3.png

출력

['001.jpg', '010.png', '011.png', '002.jpg', '003.png']
files = input().split()

print(list(map(lambda x: "{0:03d}.{1}".format(int(x.split('.')[0]),x.split('.')[1]) ,files)))

이건 좀 오랫동안 지긋지긋하게 풀줄 알았는데 생각보다 빨리 풀려서 재바르게 복기 차원에서 풀이를 주절주절 적어봐야겠다.

일단 문제에서 요구하는 것은 들어간 숫자들이 3자리 수로 나오게 출력되게 만들라는 것인데 이를 lambda식을 이용하여 print문 안에 넣으라는 것이었다

일단 files로 들어오는 애들은 split()으로 공백을 통해 끊겨 여러 요소를 가진 리스트로 생길 것이다
그렇다면 각자의 요소들을 또 쪼개 줘야하는데 이때 split('.')을 통해 하나의 str안을 또 쪼개줘야한다.

ex)
x = '7.ronaldo'
x.split('.')[0] =  '7'
x.split('.')[1] = 'Ronaldo'

이렇게 하면 첫번째로 우리가 세자리수로 바꾸고 싶은 숫자는 str으로 나오기 때문에 이를 int를 이용하여 바꿔야 한다.

int(x.split('.')[0]

이런식으로 말이다.
그렇다면 3자리수를 나타내야 하니 formatting 식을 써보자
일단 기본적으로 00x.str 의 형태를 먼저 만들어 준다.

"{0:03d}.{1}".format()

그리고 위에 적어놓은 split개념을 format()안에 넣어주자

"{0:03d}.{1}".format(int(x.split('.')[0]), x.split('.')[1])

이제 이녀석을 lambda 식을 통해 함수안에 넣고 적용할 요소를 선정해주자

lambda x:"{0:03d}.{1}".format(int(x.split('.')[0]), x.split('.')[1]), files

이렇게하면 files는 list1개에 여러가지 요소를 가진것이기 때문에 map 함수를 통해 이를 반복시켜주자

map(lambda x:"{0:03d}.{1}".format(int(x.split('.')[0]), x.split('.')[1]), files)

출력을 List로 요구했으니 그냥 마지막에 덮어주자

list(map(lambda x:"{0:03d}.{1}".format(int(x.split('.')[0]), x.split('.')[1]), files))

애를 print문에 넣어주니 문제가 해결되었다.

지긋지긋하게 코딩도장을 계속보면서 vs code에 정리한것을 한번 복습차원에서 다시 복사하여 정리하는 중인데
뭔가 이제는 알 것 같지만 .. 이걸 체득하려면 얼마나 써봐야할까 싶기도하다.
예전에 크롤링을 만들때 "아니 이건 뭐야" 했던 부분에 대한 개념이 쌓여서 기분이 좋긴한데 시간대비 너무 많은 양을 보고 있나 싶기도하고 여튼

끝!

profile
알고리즘 풀이를 담은 블로그입니다.

0개의 댓글