Unit 32. 람다 표현식 사용하기

람다 표현식은 식 형태로 되어 있다고 해서 람다 표현식 이라고 부른다. 함수를 간편하게 작성할 수 있어서 다른 함수의 인수로 넣을 때 주로 사용한다. 

 

32.1 람다 표현식으로 함수 만들기

다음은 숫자를 받은 뒤 10을 더해서 반환하는 함수 이다.

람다 표현식은 lambda에 매개변수를 저장하고 :(클론) 뒤에 반환 값으로 사용할 식을 지정한다. 이 함수를 람다 표현식으로 작성하면 다음과 같다. 

실행을 해보면 함수 객체가 나오고, 이 상태로는 함수를 호출할 수 없다. 람다 표현식은 이름이 없는 익명 함수이기 때문이다. lamda로 만든 익명 함수를 호출하려면 다음과 같이 람다 표현식을 변수에 할당해야 한다.

위 람다 표현식은 매개변수로 x를 받고, x에 10을 더해 반환한다는 뜻이다.

 

32.1.1 람다 표현식 자체를 호출하기

람다표현식은 변수에 할당하지 않고, 람다표현식 자체를 괄호로 묶고, 다시 괄호를 붙여 괄호 안에 인수를 넣어 호출할 수 있다.

 

32.1.2 람다 표현식 안에서는 변수를 만들 수 없다

람다 표현식은 변수 표현식 안에서 새 변수를 만들 수 없다. 반환값 부분은 변수의 생성 없이 한 줄로 표현할 수 있어야 한다. 새 변수를 만드는 코드를 넣으면 에러를 발생한다.

표현식 밖에서 선언된 변수는 사용할 수 있다.

 

31.1.3 람다 표현식을 인수로 사용하기

def로 만든 함수에서 map을 사용하여 리스트를 plus_ten() 함수에 넣으면 매개변수에 10을 더해서 반환하므로 리스트의 각 요소들은 10씩 더해진다. 

map의 함수 자리에 람다 표현식을 넣어도 된다.

람다 표현식을 사용하면 세줄이였던 위의 코드를 1줄로 줄일 수 있다.

람다 표현식으로 매개변수가 없는 함수를 만들때는 lamba뒤에 아무것도 지정하지 않고 클론을 붙인 후 반환값을 지정한다.

 

32.2 람다 표현식과 map, filter, reduce 함수 사용하기

32.2.1 람다 표현식에 조건부 표현식 사용하기

람다 표현식에 조건부 표현식을 사용하는 방법은 다음과 같다.

lambda 매개변수들: 식1 if 조건식 else 식2

다음은 map을 사용하영 3의 배수를 문자열로 변환하는 것이다.

리스트의 요소를 각각 처리했기 때문에 반환값도 요소로 나온다. 요소가 3의 배수일 때는 문자열로 변환하였다.

람다 표현식 안에서 조건부 표현식(if, else)을 사용할 때는 클론을 붙이지 않는다. 조건부 표현식은 '식1 if 조건식 else 식2' 형식으로 사용하며 조건식이 참이면 식1을, 거짓이면 식2 를 사용한다. 람다 표현식에서 if를 사용하면 반드시 else를 사용해야 한다. else를 사용하지 않으면 에러가 발생한다.

람다 표현식에서는 elif를 사용할 수 없다. 따라서 람다 표현식에서는 다음과 같이 if를 연달아서 사용해야 한다. 

lambda 매개변수들: 식1 if 조건식1 else 식2 if 조건식2 else 식3

다음은 리스트에서 1은 문자열로 변환하고, 2는 실수로 변환하고, 3이상의 수는 10을 더하는 코드이다.

식이 알아보기 복잡하기 때문에 이와 같은 경우에는 코드의 길이가 길어지더라도 알아보기 쉽게 작성하는것이 좋다.

 

32.2.2 map에 객체를 여러 개 넣기

map은 리스트 등 반복 가능한 객체를 여러개 넣을 수 있다. 다음은 두 리스트의 요소들을 곱해서 새로운 리스트를 만든다.

리스트 두개를 처리할 때는 람다 표현식에서 매개변수를 두 개 지정하고, 리스트 두 개를 콤마로 구분해서 넣어준다. 람다 표현식의 매개변수의 개수에 맞게 반복 가능한 객체도 콤마로 구분하여 넣어주면 된다.

 

32.2.3 filter 사용하기

filter는 반복 가능한 객체에서 특정 조건에 맞는 요소만 가져온다. filter에 지정한 함수가 True를 반환할 때만 해당 요소를 가져온다.

다음은 def로 함수를 만들어 리스트에서 5보다 크고 10보다 작은 숫자만 가져오는 함수이다.

함수 f를 람다표현식으로 만들면 다음과 같다. 

람다 표현식에 조건식을 넣어 True인 요소만 가져오도록 하였다.

 

32.2.4 reduce 사용하기

reduce는 반복 가능한 객체의 각 요소를 지정된 함수로 처리한 뒤 이전 결과와 누적하여 반환하는 함수이다.

reduce는 내장함수가 아니라 functools모듈에 있다.

다음은 리스트에 저장된 요소를 순서대로 더한 뒤 누적한 결과를 반환하는 코드이다.

함수 f를 람다 표현식으로 만들어 reduce에 넣으면 다음과 같다.

매개변수 x,y를 지정하고 x와 y를 더한 값을 반환하도록 하였다.

 

32.3 퀴즈 

정답은 d 이다.

정답은 b이다.

정답은 c이다.

 

32.4 연습문제: 이미지 파일만 가져오기

정답은 다음 코드와 같다.

list(filter(lambda x: x.find('jpg') != -1 or x.find('png') != -1, files))

find()메서드는 찾는 문자열이 있으면 그 문자열의 인덱스를 반환하고, 없으면 -1을 반환한다. 둘중 하나만 참이 되면 되므로 or연산자를 사용하여 람다 표현식으로 조건식이 True 가 나오는 요소들을 가져왔다.

 

32.5 심사문제: 파일 이름을 한꺼번에 바꾸기 

정답은 다음 코드와 같다.

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

files의 각 요소들을 .을 기준으로 나누어 숫자는 0을 채운 3자리로 만들어 다시 확장자를 더해서 리스트에 담았다.

 

Unit 33. 클로저 사용하기

33.1 변수의 사용 범위 알아보기 

파이썬 스크립트에서 변수를 만들면 다음과 같이 함수 안에서도 사용할 수 있다.

x = 10

def foo():
    print(x)

foo()
print(x)

위 변수 x처럼 함수를 포함하여 스크립트 전체에서 접근할 수 있는 변수를 전역변수 라고 하며, 전역 변수에 접근할 수 있는 범위를 전역 범위라 한다.

x를 함수 안에 지정하면 함수 밖에서는 x를 출력할 수 없다.

def foo():
    x = 10
    print(x)

foo()
print(x)

x가 정의되지 않았다는 에러가 발생한다. 변수 x는 함수 안에서 만들어 졌기 때문에 함수 안에서만 접근할 수 있는 지역변수이다. 지역변수를 접근할 수 있는 범위를 지역 범위라고 한다.

 

33.1.1 함수 안에서 전역 변수 변경하기

x = 10

def foo():
    x = 20
    print(x)

foo()
print(x)

위 코드는 함수 안에서 x의 값을 20으로 변경했지만, 함수 밖에서 출력하면 10이 나왔다. 함수 안의 x는 그 함수의 지역 변수이다. 함수 밖의 x와 함수 안의 x는 이름만 같을 뿐 서로 다른 변수이다. 

함수 안에서 전역변수의 값을 변경하려면 global이라는 키워드를 사용해야 한다. 

x = 10

def foo():
    global x
    x = 20
    print(x)

foo()
print(x)

함수 안에서 변수를 global로 지정하면 전역 변수로 사용할 수 있고, 해당 전역 변수가 없으면 해당 변수는 전역변수가 된다.

def foo():
    global x
    x = 20
    print(x)

foo()
print(x)

네임 스페이스 

파이썬에서 변수는 네임 스페이스에 저장된다. locals함수를 사용하면 현제 네임스페이스를 딕셔너리 형태로 출력할 수 있다.

다음과 같이 함수 내에서 locals함수를 사용하면 함수의 지역 범위 내의 네임스페이스를 출력할 수 있다.

 

33.2 함수 안에서 함수 만들기 

다음과 같이 def로 함수를 만들고, 그 안에서 다시 def로 함수를 만들면 된다.

def 함수이름1():
    코드
    def 함수이름2():
        코드

다음은 함수 안의 함수로 문자열을 출력하는 함수이다. 

def print_hello():
    hello = 'hello world'
    def print_message():
        print(hello)
    print_message()

print_hello()

print_hello 함수 안에 print_message 함수를 만들었다. hello변수는 print_hello 안에 있으며, print_message함수에서 hello변수를 출력한다. print_hello 함수 안에서는 print_message 함수를 실행한다. 두 함수가 실제로 동작하려면 print_hello를 호출하면 print_hello > print_message 순서로 실행된다.

 

33.2.1 지역변수의 범위 

위의 코드에서 안쪽 함수 print_message에서는 바깥쪽 함수 print_hello의 지역변수 hello를 사용할 수 있다. 바깥쪽 함수의 지역 변수는 그 안에 속한 모든 함수에서 접근할 수 있다.

 

33.2.2 지역변수 변경하기

def A():
    x = 10
    def B():
        x = 20
    B()
    print(x)

A()

A함수에서 변수 x에 10을 할당하고, A안의 B함수에서 변수 x에 20을 할당하고 B를 호출하고 x를 출력하는 A함수를 실행했다. 결과값은 10이 나왔다. B함수에서 바깥쪽 함수의 지역변수 x를 변경하는 것이 아니라 안쪽 함수 B에서 이름이 같은 지역변수 x를 새로 만드는 것이다. 파이썬에서는 함수에서 변수를 만들면 항상 현재 함수의 지역변수가 된다.

현재 함수 바깥쪽에 있는 지역변수의 값을 변경하려면 nonlocal 키워드를 사용해야 한다.

def A():
    x = 10

    def B():
        nonlocal x
        x = 20
    B()
    print(x)

A()

nonlocal키워드로 바깥쪽 함수의 지역변수의 값을 변경한 것을 확인할 수 있다.

nonlocal은 현재 함수의 지역변수가 아니라는 뜻이기 때문에 바깥쪽 함수의 지역변수를 사용한다.

 

33.2.3 nonlocal이 변수를 찾는 순서

def A():
    x = 10
    y = 100
    def B():
        x = 20
        def C():
            nonlocal x
            nonlocal y
            x += 30
            y += 300
            print(x)
            print(y)
        C()
    B()

A()

함수 C에서 nonlocal 키워드를 사용하게 되면 변수 x는 바깥쪽의 b 함수의 지역변수 x = 20을 사용하게 된다. 변수 y는 B 함수에는 없기 때문에 한단계 더 바깥으로 나가서 A 함수의 y 변수를 사용하게 된다. nonlocal은 가까운 함수부터 지역변수를 찾고, 지역변수가 없으면 계속 밖으로 나가서 찾는다.

nonlocal 키워드를 사용했는데, 바깥쪽 함수에 nonlocal로 지정한 변수가 없다면 위와 같은 에러를 발생시킨다.

 

33.2.4 global로 전역변수 사용하기

x = 1

def A():
    x = 10
    def B():
        x = 20
        def C():
            global x
            x += 30
            print(x)
        C()
    B()

A()

함수가 몇단계든 상관없이 global키워드를 사용하면 무조건 전역변수를 사용하게 된다. 함수 C에서만 전역변수를 사용했기 때문에 x+=30 은 31이된다.

 

33.3 클로저 사용하기

다음은 함수 바깥 쪽에 있는 지역변수 a,b를 사용하여 a * x + b를 계산한 뒤 반환한다.

def calc():
    a = 3
    b = 5
    def mul_add(x):
        return a * x + b
    return mul_add

c = calc()
print(c(1), c(2), c(3), c(4), c(5))

calc함수에 변수 a,b를 만들고 3과 5를 저장하였다. mul_add 함수에서는 a 와 b를 사용하여 a * x + b를 반환한다. 이후 calc 함수에서 mul_add 함수 자체를 반환한다. 함수를 반환할 때는 함수 이름만 반환하며 괄호를 붙이지 않는다. 

함수 calc를 호출하고 반환값을 c에 저장하면 calc에서 mul_add를 반환했으므로 c에는 함수 mul_add가 들어간다.

print(c)를 통해 c를 출력해보면 mul_add함수 자체만 들어간 것을 알 수 있다.

그리고 c에 숫자를 넣어 호출하면 계산식에 따라 연산한 값이 출력된다. 

함수 calc가 끝났는데도, calc의 지역변수 a,b를 사용하여 계산을 하고 있다. 이렇게 함수를 둘러싼 환경을 유지하다가 함수를 호출할 때 다시 꺼내서 사용하는것을 클로저 라고 한다.

c에 저장된 함수가 클로저 이기 때문에 위 그림처럼 calc함수 내의 지역변수를 가져와서 사용할 수 있다. 

클로저를 사용하면 프로그램의 흐름을 변수에 저장할 수 있고, 지역변수와 코드를 묶어서 사용하고 싶을 때 활용한다. 

또한 클로저에 속한 지역변수는 밖에서 직접 접근할 수 없기 때문에 데이터를 숨기고 싶을때 사용한다. 함수 외부에서 데이터의 변경이 일어나면 안될 때 사용해도 좋을것 같다.

 

33.3.1 lambda로 클로저 만들기

클로저는 다음과 같이 람다로도 만들 수 있다.

def calc():
    a = 3
    b = 5
    return lambda x: a * x + b

c = calc()
print(c(1),c(2),c(3))

람다는 이름이 없는 익명함수 이고, 클로저는 함수를 둘러싼 환경을 유지했다가 나중에 다시 사용하는 함수를 뜻한다.  

 

33.3.2 클로저의 지역변수 변경하기

클로저의 지역변수를 변경하고 싶으면 다음과 같이 nonlocal을 사용한다.

def calc():
    a = 3
    b = 5
    total = 0
    def mul_add(x):
        nonlocal total
        total = total +  a * x + b
        print(total)
    return mul_add

c = calc()
c(1)
c(2)
c(3)

a * x + b의 결과를 calc함수의 지역변수 total에 누적한다.

 

33.4 퀴즈

정답은 b,d이다. 함수 안에서 전역변수의 값을 바꿀 때는 global을 사용하고, 함수 안에 함수가 여러 단계 있어도 전역변수를 사용할 수 있다.

정답은 c,d이다. 지역변수는 함수 바깥쪽에서 사용할 수 없고, nonlocal을 이용하면 두단계 이상 바깥쪽에 있는 지역변수도 사용할 수 있다.

정답은 c,d이다. 람다는 익명함수를 의미하므로 다른 의미이다. 클로저는 람다 표현식으로 만들 수 있다.

 

33.5 연습문제 : 호출 횟수를 세는 함수 만들기

정답은 다음코드와 같다.

        nonlocal i
        i += 1
    	return i
    return count

conunt 함수에서 counter 함수의 지역변수 i를 사용하여 counte 함수가 실행될 때마다 1씩 누적하고 누적한 값을 반환하며 counter 함수에서 count함수를 반환하게 하였다.

 

33.6 심사문제: 카운트 다운 함수 만들기

정답은 다음 코드와 같다.

    i = n+1
    def down():
        nonlocal i
        i -= 1
        return i
    return down

입력한 값을 포함하여 숫자를 세야 하므로 countdown 함수의 지역변수 i를 입력값보다 1 크게 하고, down함수를 만들어 i를 1씩 감소시킨 후 반환하였다. countdown 함수는 down 함수를 반환하였다.

 

Unit 34. 클래스 사용하기

클래스는 객체를 표현하기 위한 문법이다. 클래스는 게임속의 다양한 직업들, 웹 브라우저 에서 사용하는 스크롤바, 버튼, 체크박스 등 같은 개념을 사용하는 객체를 묶은 것이다. 객체는 스크롤바, 버튼, 체크박스처럼 특정한 개념이나 모양으로 존재하는 것이다. 프로그래밍에서 객체를 만들 때 클래스를 사용한다.

게임의 캐릭터를 클래스로 표현하면 캐릭터는 체력, 마나, 공격력 등이 필요하며 칼로 찌르기, 베기 등의 스킬이 있어야한다. 

체력, 마나, 공격력 등을 속성(attribute)라 하고, 찌르기, 베기 등의 스킬을 메서드(method)라 한다.

이런 프로그래밍 방법을 객체 지향 프로그래밍이라고 한다. 객체 지향 프로그래밍은 복잡한 문제를 잘게 나누어 객체로 만들고, 객체를 조합하여 문제를 해결한다. 기능을 개선하고 발전시킬때도 해당 기능을하는 클래스만 수정하면 되서 유지보수도 효율적이다.

지금까지 사용했던 숫자, 문자, 리스트, 딕셔너리 등도 다 각각의 객체이다.

 

34.1 클래스와 메서드 만들기

클래스는 class에 이름을 지정하고 클론을 붙인 뒤 다음줄 부터 def로 메서드를 작성하면 된다. 메서드는 클래스 안에 들어있는 함수들이다.

파이썬에서는 보통 클래스 이름은 대문자로 시작한다. 메서드 작성 방법은 함수와 같고, 코드는 반드시 들여쓰기를 해야 한다. 메서드의 첫번째 매개변수는 반드시 self를 지정해야 한다.

class 클래스이름:
    def 메서드(self):
        코드

다음은 간단한 사람 클래스 이다.

다음과 같이 클래스에 괄호를 붙인뒤 변수에 할당하면 사용할 수 있다.

변수 james를 클래스의 인스턴스라고 한다. 클래스는 특정 개념만 표현할 뿐 사용하려면 인스턴스를 생성해야 한다.

 

34.1.1 메서드 호출하기

메서드는 인스턴스를 통해 호출된다. 다음과 같이 인스턴스 뒤에 .(점)을 붙이고 메서드를 호출하면 된다.

위와 같이 인스턴스를 통해 호출하는 메서드를 인스턴스 메서드라고 인스턴스 메서드 라고 부른다.

 

34.1.2 파이썬에서 흔히 볼 수 있는 클래스

지금까지 사용한 int, list, dict등도 클래스 이다. 이 클래스로 인스턴스를 만들고, 메서드를 사용한 것이다.

위와 같은 코드들은 int 클래스로 인스턴스 a를 만들고, list클래스로 인스턴스 b를 만들고, dict클래스로 인스턴스 c를 만든 것이다. 정수를 만들 땐 int를 생략하고, 리스트, 딕셔너리는 [ ], { }을 이용하여 만들지만 이것들도 다 클래스 이다.

위 코드는 인스턴스 b에서 append라는 메서드를 호출하여 사용한 것이다.

파이썬에서는 자료형도 각각의 클래스이다. type을 사용하면 객체(인스턴스)가 어떤 클래스 인지 확인할 수 있다.

 

34.1.3 인스턴스와 객체의 차이점

객체와 인스턴스는 같은 것이다. 보통 객체만 지정할 때는 객체라고 부르고, 클래스와 연관지어서 말할 때는 인스턴스라고 부른다. 

 

빈 클래스 만들기

빈 클래스는 다음과 같이 코드 부분에 pass를 넣어주면 된다. 

class Person:
    pass

메서드 안에서 메서드 호출하기

메서드 안에서 메서드를 호출할 때는 self.메서드() 형식으로 호출해야 한다. self없이 메서드 이름만 사용하면 클래스 바깥쪽에 있는 함수를 호출한다는 뜻이 된다.

class Person:
    def greeting(self):
        print('hello')
    
    def hello(self):
        self.greeting()

특정 클래스의 인스턴스인지 확인하기

현재 인스턴스가 특정 클래스의 인스턴스인지 확인할 때는 isinstance함수를 사용한다. 특정 클래스의 인스턴스가 맞으면 True, 아니면 False를 반환한다.

isinstance는 주로 객체의 자료형을 판단할 때도 사용한다.

 

34.2 속성 사용하기

속성을 만들 때는 __init__메서드 안에서 self.속성 에 값을 할당한다.

class 클래스이름: 
    def __init__(self):
        self.속성 = 값
class Person:
    def __init__(self):
        self.hello = 'hello! '

    def greeting(self):
        print(self.hello)

james = Person()
james.greeting()

__init__ 메서드는 james = Person() 처럼 클래스에 괄호를 붙여서 인스턴스를 만들 때 호출되는 특별한 메서드이다. __init__메서드는 인스턴스(객체)를 초기화 한다.

__init__처럼 앞뒤로 __(밑줄두개)가 붙은 메서드는 파이썬이 자동으로 호출해주는 메서드로 스페셜 메서드 또는 매직메서드 라고 부른다.

greeting메서드 에서는 print로 self.hello를 출력하였다.

Person으로 객체를 만들고, greeting를 호출하면 self.hello에 저장된 hello! 가 출력된다.

속성은 __init__메서드 안에서 만들고, self에 .(점)을 붙여서 할당한다. 클래스 안에서 속성을 사용할 때도 self에 .(점)을 붙여서 사용한다.

 

34.2.1 self의 의미

self는 인스턴스 자기 자신을 의미한다. 인스턴스가 생성될 때 self.hello처럼 자기 자신에 속성을 추가하였다.

위 클래스에서 __init__의 매개변수 self에 들어가는 값은 Person이다.  self가 완성된 뒤에는 james가 할당된다. 이후 메서드를 호출하면 현재 인스턴스가 자동으로 매개변수 self에 들어온다. 그래서 greeting에서 self.hello의 속성을 출력할 수 있었던 것이다.

위 설명을 그림으로 나타내면 다음과 같다.

 

34.2.2 인스턴스를 만들 때 값 받기

다음과 같이 __init__메서드에서 self 다음에 값을 받을 매개변수를 지정하고, 매개변수를 self.속성에 넣어준다.

class 클래스이름:
    def __init__(self, 매개변수1, 매개변수2):
        self.속성1 = 매개변수1
        self.속성2 = 매개변수2

다음은 Person 클래스로 인스턴스를 받을 때 매개변수로 이름, 나이, 주소를 받은 것이다.

class Person:
    def __init__(self, name, age, address):
        self.hello = 'hello!'
        self.name = name
        self.age = age
        self.address = address

    def greeting(self):
        print('{} I am {}.'.format(self.hello, self.name))

maria = Person('maria', 20, 'seoul')
maria.greeting()

print(maria.name)
print(maria.age)
print(maria.address)

__init__메서드 에서는 매개변수로 name, age, address를 받고 각각 그대로 self에 넣어서 속성으로 만들었다.

greeting메서드는 인사를 하고, self.name으로 name 속성에 접근하여 이름을 출력하도록 하였다.

Person() 괄호 안에 이름, 나이, 주소 정보를 넣어 maria인스턴스를 만들었다. 괄호 안에 넣은 값은 __init__ 메서드의 self다음 매개변수로 차례대로 들어간다.

클래스 바깥에서 속성에 접근할 때는 인스턴스.속성 의 형식으로 접근이 가능하다. maria.name, maria.age등과 같은 방식으로 속성에 접근한다. 이렇게 인스턴스를 통해 접근하는 속성을 인스턴스 속성이라고 한다.

 

클래스의 위치인수, 키워드 인수

클래스로 인스턴스를 만들 때 위치 인수와 키워드 인수를 사용할 수 있다. 규칙은 함수와 동일하다. 위치인수와 리스트 언패킹을 사용하려면 다음과 같이 *args를 사용하면 된다. 매개변수에서 값을 가져오려면 args[0]과 같은 방식으로 사용해야 한다.

class Person:
    def __init__(self, *args):
        self.name = args[0]
        self.age = args[1]
        self.addr = args[2]
 
maria = Person(*['maria', 20, 'seoul'])

키워드 인수와 딕셔너리 언패킹을 사용하려면 **kwargs를 사용하면 된다. 매개변수에서 값을 가져오려면 kwargs['name']처럼 사용해야 한다.

class Person:
    def __init__(self, **kwargs):
        self.name = kwargs['name']
        self.age = kwargs['age']
        self.address = kwargs['addr']
 
maria1 = Person(name='maria', age=20, addr='seoul')
maria2 = Person(**{'name': 'maria', 'age': 20, 'addr': 'seoul'})

 

인스턴스를 생성한 뒤에 속성 추가하기, 특정 속성만 허용하기

다음과 같이 클래스로 인스턴스를 만든 뒤에 인스턴스.속성 = 값 의 형식으로 속성을 계속 추가할 수 있다.

이렇게 추가한 속성은 해당 인스턴스에서만 사용할 수 있다. 클래스로 다른 인스턴스를 만들면 추가한 속성은 없다.

__init__메서드가 아닌 다른 메서드에서도 속성을 추가할 수 있지만 이 경우 메서드를 호출해야 속성이 생성된다.

greeting메서드를 호출하기 전까지는 hello 속성이 없다.

특정 속성만 허용하고, 다른 속성들은 제한하고 싶을때 __slots__에 허용할 속성을 리스트로 넣어주면 된다. 속성 이름은 반드시 문자열 이여야 한다.

리스트에 들어있는 이름 외에 다른 이름을 가진 속성은 생성할 수 없다.

 

34.3 비공개 속성 사용하기

class Person:
    def __init__(self, name, age, address):
        self.hello = 'hello!'
        self.name = name
        self.age = age
        self.address = address

위의 클래스에는 hello, name, age, address의 속성이 있고, 이 속성들은 메서드에서 self로 접근할 수 도 있고, 인스턴스.속성의 형식으로 클래스 밖에서도 접근할 수 있다.

클래스 안에서만 접근 가능하고, 밖에서는 접근할 수 없는 비공개 속성이 있다. 

비공개 속성은 이름이 __(밑줄 두 개)로 시작해야 한다. 단, __속성__ 처럼 밑줄 두 개가 양 옆에 왔을 때는 비공개 속성이 아니다.

class 클래스이름:
    def __init__(self, 매개변수):
        self.__속성 = 값

다음은 Person클래스에 비공개 속성 __wallet을 넣은것이다.

class Person:
    def __init__(self, name, age, address, wallet):
        self.name = name
        self.age = age
        self.address = address
        self.__wallet = wallet
 
maria = Person('maria', 20, 'seoul', 10000)
maria.__wallet -= 10000   

위 코드를 실행하면 다음과 같은 에러가 발생한다.

__wallet는 비공개 속성이기 때문에 클래스 밖에서는 접근할 수 없다. __wallet에 접근하려면 다음과 같이 클래스 안에서 접근해야 한다.

class Person:
    def __init__(self, name, age, address, wallet):
        self.name = name
        self.age = age
        self.address = address
        self.__wallet = wallet
    
    def pay(self, amount):
        self.__wallet -= amount
        print('이제 {}원 남았네요'.format(self.__wallet))
 
maria = Person('maria', 20, 'seoul', 10000)
maria.pay(3000)

위 코드와 같이 비공개 속성은 클래스 안의 메서드에서는 접근할 수 있다.

보통의 프로그램에서는 지갑에 든 돈은 굳이 밝힐 필요가 없기 때문에 다음과 같이 돈이 얼마인지 확인하고, 모자라면 쓰지 못하는 식으로 만든다.

    def pay(self,amount):
        if amount > self.__wallet:
            print('돈이 모자랍니다')
            return
        self.__wallet -= amount

 

비공개 메서드 사용하기

메서드도 이름이 __(밑줄 두 개)로 시작하면 클래스 안에서만 호출할 수 있는 비공개 메서드가 된다.

 

34.4 퀴즈

정답은 d이다.

정답은 __init__ 이다.

정답은 e이다.

정답은 c이다.

 

34.5 연습문제: 게임 캐릭터 클래스 만들기

정답은 다음 코드와 같다.

class Knight:
    def __init__(self, health, mana, armor):
        self.health = health
        self.mana = mana
        self.armor = armor
        
    def slash(self):
        print('베기')

 

34.6 심사문제: 게임 캐릭터 클래스 만들기

정답은 다음 코드와 같다.

class Annie:
    def __init__(self, health, mana, ability_power):
        self.health = health
        self.mana = mana
        self.ability_power = ability_power
    def tibbers(self):
        print('티버: 피해량 {}'.format(self.ability_power * 0.65+400))

Unit 27. 파일 사용하기

27.1 파일에 문자열 쓰기, 읽기

27.1.1 파일에 문자열 쓰기

파일에 문자열을 쓸때는 open()함수로 파일을 열어 파일 객체를 얻은 뒤에 write메서드를 사용한다. 파일 쓰기가 끝나면 close()로 파일 객체를 닫아야 한다.

파일객체 = open(파일이름, 파일모드)
파일객체.write('문자열')
파일객체.close()

 

다음은 파일을 열어 'hello world' 라는 문자열을 작성하는 소스코드이다. 

file = open('hello.txt', 'w')
file.write('hello world')
file.close()

이 소스코드를 실행하면 소스코드 파일이 있는 폴더에 hello.txt파일이 생성되고, 파일을 열어보면 작성한 문자열이 있는 것을 확인할 수 있다.

파일을 사용하려면 먼저 open함수로 파일을 열어 파일 객체를 얻어야 한다. 열 파일의 이름을 지정하고 파일 모드를 지정하는데 위 코드에서 지정했던 'w'는 파일에 내용을 쓸때 사용하는 것이고, write의 w이다. 

파일 객체를 얻었기 때문에 write() 메서드로 파일에 문자열을 쓸 수 있다. 파일 쓰기가 끝나면 close()로 파일 객체를 닫아줘야 한다.

 

27.1.2 파일에서 문자열 읽기

파일을 읽을 때도 open()함수로 파일을 열어 파일 객체를 얻은 뒤 read()메서드로 파일 내용을 읽는다. 파일을 열때 파일 모드는 'r'로 읽기모드를 의미한다.

file = open('hello.txt', 'r')
s = file.read()
print(s)
file.close()

open을 사용하여 파일을 읽기모드로 열고, read의 반환값을 변수에 저장하여 print로 그 변수를 출력한 후 close로 파일 객체를 닫았다. 

 

27.1.3 자동으로 파일 객체 닫기

with as를 사용하면 파일을 사용한 뒤 자동으로 파일 객체를 닫아준다. 다음과 같은 형식으로 사용한다.

with open(파일이름, 파일모드) as 파일객체:
    코드

다음 코드는 위의 파일 읽는 코드를 with as를 이용하여 작성한 것이다.

with open('hello.txt', 'r') as file:
    s = file.read()
    print(s)

 

27.2 문자열 여러줄을 파일에 쓰기, 읽기

27.2.1 반복문으로 문자열 여러줄을 파일에 쓰기

문자열 여러줄을 파일에 쓰려면 다음과 같이 반복문을 이용하면 된다.

with open('hello.txt', 'w') as file:
    for i in range(3):
        file.write('hello world {}\n'.format(i))    

파일에 문자열을 여러 줄로 저장할 때는 문자열 끝에 개행문자(\n)을 꼭 지정해줘야 한다. 개행문자를 지정해주지 않으면 파일을 쓸 때 문자열이 모두 한 줄로 붙어서 저장된다. 

 

27.2.2 리스트에 들어있는 문자열을 파일에 쓰기 

리스트에 들어있는 문자열을 파일에 쓸 때는 writelines()를 사용한다. 

lines = ['hello world\n', 'python is fun\n', 'project h4c is good\n']

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

writelines()는 리스트에 들어있는 문자열을 파일에 쓴다. 위와 같이 여러줄로 쓰고 싶으면 리스트 각 요소의 끝에 개행문자(\n)을 붙여줘야 한다. 개행문자를 붙이지 않으면 모든 요소가 한 줄로 붙어서 저장된다.

 

27.2.3 파일의 내용을 한 줄씩 리스트로 가져오기

readlines()는 파일의 내용을 한 줄씩 리스트 형태로 가져온다.

with open('hello.txt', 'r') as file:
    lines = file.readlines()
    print(lines)

 

27.2.4 파일의 내용을 한 줄씩 읽기

파일 내용을 한 줄씩 순차적으로 읽을때는 readline()을 사용한다.

with open('hello.txt', 'r') as file:
    line = None
    while line != '':
        line = file.readline()
        print(line.strip('\n'))

readline()으로 파일을 읽을 때는 파일에 문자열이 몇줄이나 있는지 모르기 때문에 while 반복문을 사용해야 한다. while 반복문은 빈문자열이 아닐 때 계속 반복한다. 빈 문자열이 이 아닐때 반복해야 하기 때문에 line은 처음에 빈 문자열이 아니라 None로 초기화 한다. 빈 문자열로 초기화 하면 반복문을 진행할 수 없기 때문이다. 문자열을 출력할 때는 strip로 개행문자를 삭제한다. 개행문자를 삭제하지 않으면 문자열 안에 들어있는 개행문자와 print()함수 때문에 문자열 한 줄을 출력할 때 마다 빈 줄이 계속 출력된다.

 

27.2.5 for 반복문으로 파일의 내용을 줄 단위로 읽기

for 반복문으로 while반복문 보다 간단하게 파일의 내용을 읽을 수 있다.

with open('hello.txt', 'r') as file:
    for line in file:
        print(line.strip('\n')) 

for 반복문에 파일 객체를 지정하면 반복을 할 때 마다 파일의 내용을 한 줄씩 읽어서 변수에 저장할 수 있다.

파일 객체는 이터레이터이다. 따라서 변수 여러 개에 저장하는 언패킹도 가능하다. 할당할 변수의 개수와 파일에 저장된 문자열의 줄 수가 같아야 한다.

 

27.3 파이썬 객체를 파일에 저장하기, 가져오기

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

 

27.3.1 파이썬 객체를 파일에 저장하기

피클링은 pickle 모듈의 dump메서드를 사용한다.

import pickle

name = 'phulasso'
age = 20
address = 'Seoul'
scores = {'korean': 90, 'english': 95, 'mathematics': 85, 'science': 82}

with open('james.p', 'wb') as file:
    pickle.dump(name, file)
    pickle.dump(age, file)
    pickle.dump(address, file)
    pickle.dump(scores,file)

pickle.dump로 객체를 저장할 때는 파일 모드를 'wb'로 지정해야 한다. b는 바이너리(binary)의 b이다. 바이너리 파일은 컴퓨터가 처리하는 파일 형식이기 때문에 사람이 알아보기 힘들다.

 

27.3.2 파일에서 파이썬 객체 읽기

언피클링은 pickle모듈의 load를 사용한다. 언피클링 할때는 파일 모드를 바이너리 읽기 모드인 'rb'로 설정해야 한다.

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)

파일에서 객체를 가져올때도 pickle.load를 4번 사용하여 저장한 순서대로 가져온다. 

파일 모드는 r, w 이외에도 추가 'a', 배타적생성 'x'도 있다. 추가 모드는 이미 있는 파일에서 파일 끝에 내용을 추가할 때 사용하고, 배타적 생성 모드는 파일이 이미 있으면 에러를 발생하고, 파일이 없으면 파일을 만든다. 파일 형식도 함께 지정할 수 있는데 t는 텍스트 모드로 r, w와 같은 방식으로 생략이 가능하며, 사람이 읽을 수 있는 텍스트 파일이다. b는 바이너리 모드로 피클링을 사용하거나, 바이너리 데이터를 직접 지정할 때 사용한다. +는 파일을 읽기/쓰기 모드로 연다. 아래 그림과 같이 다양한 방식으로 조합하여 사용할 수 있다.

 

27.4 퀴즈

정답은 d이다.

정답은 c이다.

정답은 b이다.

 

27.5 연습문제: 파일에서 10자 이하인 단어 개수 세기

정답은 다음 코드와 같다.

with open('words.txt', 'r') as file:
    count = 0
    words = file.readlines()
    for i in words:
        if len(i.strip('\n')) <= 10:
            count += 1

주어진 파일을 일기 모드로 읽어서 리스트로 가져온 후 반복문으로 리스트의 각 요소들이 개행문자를 제외하고 10자 이하인지 확인하여 10자 이하면 count에 1을 더해주었다.

27.6 심사문제: 특정 문자가 들어있는 단어 찾기

정답은 다음 코드와 같다.

with open('words.txt', 'r') as f:
    s = list(f.readline().split())
    w_list = []
    for i in s:
        if 'c' in list(i.strip(',.')):
            w_list.append(i.strip(',.'))
    for i in w_list:
        print(i)

파일에서 문자열을 읽어와 공백을 기준으로 나눈 리스트로 만들고, 리스트의 각 요소에 c가 들어있는지 확인해서 들어 있으면 다른 리스트에 , 와 . 을 없애고 추가한다. 추가한 다른 리스트를 출력하면 문제를 해결할 수 있다.

 

Unit 28. 회문 판별과 N-gram만들기

회문은 유전자 염기서열 분석에 많이 쓰고, N-gram은 빅데이터 분석, 검색 엔진에서 많이 사용된다. 

 

28.1 회문 판별하기

회문은 순서를 거꾸로 읽어도 제대로 읽은 것과 같은 단어와 문장을 말한다. 예를 들면 level, sos, rotator, nurses run 등이 있다.

문자열이 회문인지 판별하려면 첫번째 글자와 마지막 글자가 같고, 안쪽으로 한 글자씩 좁혔을 때 글자가 서로 같으면 회문이다.

 

28.1.1 반복문으로 문자 검사하기

다음은 반복문으로 문자열의 각 문자들을 검사해서 회문인지 판단하는 코드이다.

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

is_palindrome = True
for i in range(len(word) // 2):
    if word[i] != word[-1 - i]:
        is_palindrome = False
        break

print(is_palindrome)

회문이면 True를 출력하고, 회문이 아니면 False를 출력한다.

회문 판별은 문자열의 길이를 기준으로 하며 문자열을 절반으로 나누어 왼쪽 문자와 오른쪽 문자가 같은지 구분한다.

다음과 같이 반복할 때 문자열 길이의 절반만큼 반복한다. 

for i in range(len(word) // 2):

문자열 길이가 홀수 일수도 있기 때문에 버림 나눗셈을 한다. 문자열이 홀수여도 가운데 글자 바로 앞까지 검사하게 된다.

반복문 안에서 왼쪽 문자와 오른쪽 문자가 다르면 회문이 아니므로 is_palindrome에 False를 넣고 반복문을 종료한다. 

왼쪽 문자와 오른쪽 문자가 같으면 반복문이 진행하며 word[i]와 word[-1 - i]로 문자열 왼쪽, 오른쪽에서 한 칸씩 안으로 들어가며 다시 비교한다.

 

 28.1.2 시퀀스 뒤집기로 문자열 검사하기

시퀀스 객체의 슬라이스를 이용하면 더 간편하게 회문을 판별할 수 있다.

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

print(word == word[::-1])

word[::-1]은 문자열 전체에서 인덱스를 1씩 감소시키며 요소를 가져 오기 때문에 문자열을 반대로 뒤집는것과 똑같다. 원래 문자열과 반대로 뒤집은 문자열이 같으면 회문이다.

 

28.1.3 리스트와 reversed 사용하기

다음과 같이 반복 가능한 객체를 반대로 뒤집는 reversed를 사용해도 회문을 판별할 수 있다.

리스트에 문자열을 넣으면 문자 하나 하나가 리스트의 요소로 들어가고 reversed로 뒤집은 문자열을 리스트에 넣어 두 리스트를 비교한다.

 

28.1.4 문자열의 join메서드와 reversed 사용하기

다음과 같이 join메서드를 사용하여 회문을 판별할 수 있다.

join은 구분자 문자열과 문자열 리스트를 연결하는 메서드이다. 위 코드에서는 빈문자열과 word를 뒤집은 객체의 요소를 연결했으므로  문자순서가 반대로 된 문자열을 얻을 수 있다. 두 문자열을 비교하여 회문을 판단할 수 있다.

 

28.2 N-gram 만들기

N-gram은 문자열에서 N개의 연속된 요소를 추출하는 방법이다. 만약 'Hello'라는 문자열을 2-gram으로 추출하면 다음과 같다. 

He
el
ll
lo

문자열 처음부터 끝까지 한 글자씩 이동하며 두글자씩 추출한다. 3-gram은 3글자 4-gram은 4글자를 추출한다.

 

28.2.1 반복문으로 N-gram 출력하기

text = 'hello'

for i in range(len(text) - 1):
    print(text[i],text[i+1], sep='')

문자열 끝에서 한글자 앞까지만 반복하며 현재 문자와 그다음 문자 두 글자씩 출력한다.

만약 3-gram이라면 반복 횟수는 range(len(text) - 2))이고, 문자열을 출력할때는 text[i], text[i+1], text[i+2]를 출력한다.

단어 단위 N-gram은 문자열을 공백으로 구분하여 출력한다. 다음은 단어단위 2-gram의 예시다.

text = 'this is python script'
words = text.split()

for i in range(len(words) - 1):
    print(words[i],words[i+1])

문자열을 공백을 기준으로 분리하여 리스트를 만들고 2-gram이기 때문에 리스트의 마지막 요소 한개 앞까지 반복하면서 현재 문자열과 그 다음 문자열을 출력한다.

 

28.2.2 zip로 2-gram 만들기

zip함수를 이용하여 2-gram을 만드는 코드는 다음과 같다.

text = 'hello'

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

 zip 함수는 반복 가능한 개체의 각 요소를 튜플로 묶어준다. text[1:]은 두번째 문자부터 마지막 문자 까지 가져 와서 text와 text[1:]을 zip으로 묶으면 문자 하나가 밀린 상태로 각 문자를 묶는다.

for로 반복하며 i[0],i[1]을 순서대로 출력하면 된다.

단어 단위의 2-gram도 같은 방식으로 만든다.

3-gram을 만드려면 zip(words, words[1:], words[2:]) 처럼 3개의 리스트를 넣으면 된다.

 

28.2.3 zip과 리스트 표현식으로 N-gram만들기

리스트 표현식을 사용하면 코드로 편하게 N-gram을 만들 수 있다.

for 로 3번 반복하면서 text[0:]부터 text[2:]까지 만들어졌다. 이 반환값을 zip에 넣으면 다음과 같다.

결과를 보면 3-gram이 아니다. zip은 반복 가능한 객체 여러개를 콤마로 구분하여 넣어줘야 한다. 하지만 ['hello', 'ello', 'llo']는 요소가 3개 들어있는 리스트 1개이다. 리스트의 각 요소를 콤마로 구분하려면 리스트 앞에 *을 붙여줘야 한다.

리스트에 *을 붙이는 방법은 리스트 언패킹 이라고 한다.

 

28.3 연습문제: 단어 단위 N-gram만들기

정답은 다음 코드와 같다.

1. text.split()
2. n > len(words)
3. zip(*[words[i:] for i in range(n)])

input로 입력받은 문자열은 공백을 기준으로 분리하여 리스트로 만들어 줘야 하며, 입력받은 숫자보다 리스트 요소 개수가 작으면 wrong를 출력하고, 리스트 표현식을 이용하여 N-gram을 만들었다.

 

28.4 심사문제: 파일에서 회문인 단어 출력하기

정답은 다음 코드와 같다.

 with open('words.txt', 'r') as f:
    words = f.readlines()
    for i in words:
        if i.strip('\n') == i.strip('\n')[::-1]:
            print(i,end='')

파일을 열어 리스트로 내용을 읽어와서 요소들을 개행문자를 제거하고 시퀀스 슬라이스를 이용해 뒤집은것과 비교해서 참인것들만 출력하였다.

 

Unit 29. 함수 사용하기

함수는 특정 용도의 코드를 한 곳에 모아 놓은 것이다. 함수를 처음에 한 번만 작성해 놓으면 나중에 필요할 때 계속 불러쓸 수 있다. print(), input()등도 파이썬에서 미리 만들어진 함수이다. 함수를 사용하면 좋은 점들은 다음과 같다.

  • 코드의 용도를 구분할 수 있다
  • 코드를 재사용 할 수 있다.
  • 실수를 줄일 수 있다.

 

29.1 Hello, world! 출력함수 만들기 

함수는 def에 함수 이름을 지정하고, ( )(괄호)와 :(클론)을 붙인 뒤 다음줄에 들여쓰기를 한 후 원하는 코드를 작성한다.

def 함수이름():
    코드

def는 define(정의하다)에서 따온 것이다.

 

29.1.1 함수 만들기

Hello, world!를 출력하는 함수는 다음과 같다.

함수 이름은 hello이고, 코드는 print()로 출력하는 코드이다.

 

29.1.2 함수 호출하기

함수는 hello()와 같이 함수 이름과 괄호를 붙여주면 사용할 수 있다. 이렇게 함수를 사용하는 것을 함수를 호출한다고 한다.

 

29.1.3 소스파일에서 함수를 만들고 호출하기

다음과 같이 코드를 작성하면 hello함수가 만들어진 후 호출되어 Hello, world!가 출력된다.

def hello():
    print('Hello, world!')

hello()

 

29.1.4 함수의 실행 순서

hello 함수의 실행 순서는 다음과 같다.

  1. 파이썬 스크립트 최초 실행
  2. hello 함수 호출
  3. hello 함수 실행
  4. print 함수 실행 및 'Hello, world!' 출력
  5. hello 함수 종료
  6. 파이썬 스크립트 종료

 

29.1.5 함수 작성과 함수 호출 순서 

함수를 만들기 전에 함수를 먼저 호출하면 에러가 발생한다.

파이썬 코드는 위에서 부터 아래로 순차적으로 실행하기 때문에 호출을 먼저하면 함수가 정의되지 않았다는 오류가 발생한다.

 

빈 함수 만들기

내용이 없는 빈함수를 만들때는 코드 부분에 pass를 넣는다.

def hello():
   pass

pass는 아무 일을 하지 않아도 함수의 틀을 유지해야 할 때 사용한다.

 

29.2 덧셈 함수 만들기

함수에 값을 받으려면 ( )(괄호) 안에 변수 이름을 지정해주면 된다. 이 변수를 매개변수 라고 한다.

def 함수이름(매개변수1, 매개변수2):
    코드

두 수를 더하는 함수는 다음과 같다. 괄호 안에 매개변수 a와 b를 지정하고, 그 다음줄에서 print로 a와b의 합을 출력한다.

add함수에 10과 20을 넣어서 호출하면 두 값을 더한 30이 나온다. 함수를 호출할 때 넣는 값을 인수 라고 부른다. add 함수의 호출 과정은 다음과 같다.

함수의 클론 바로 다음줄에 """ """(큰따옴표 3개)안에 문자열을 입력하면 함수에 대한 설명을 넣을 수 있다. 이런 방식의 문자열을 독스트링 이라고 하며, 독스트링 위에는 다른 코드가 오면 안된다. 독스트링은 ' ', " " , ''' ''' 등으로 만들어도 되지만 파이썬 코딩 스타일 가이드에서 """ """ 을 권장한다. 위의 add함수에 독스트링을 추가하면 다음과 같다.

독스트링은 함수의 설명을 기록한 것이기 때문에 함수를 사용해도 출력되지 않는다. 독스트링을 출력하려면 함수의 __doc__를 출력하면 된다.

다음과 같이 help()에 함수를 넣으면 함수의 이름, 매개변수, 독스트링을 도움말 형태로 출력한다.

 

29.3 함수의 결과를 반환하기

함수 안에서 return을 사용하면 값을 함수 바깥으로 반환한다.

def 함수이름(매개변수):
    return 반환값

두 수를 더한 값을 반환하는 add함수는 다음과 같다. 매개 변수 두개를 더한 값을 return으로 반환한다.

함수에 3과 5를 넣은 결과를 x에 저장하고 x를 출력하면 두 값을 더한 8이 나온다. return을 사용하면 값을 함수 바깥으로 반환할 수 있고, 함수에서 나온 값을 변수에 저장할 수 있다. return으로 반환하는 값은 반환값 이라고 하고, 함수를 호출해준 바깥에 결과를 알려주기 위해 사용한다.

반환값은 변수에 저장하지 않고, 바로 다른 함수에 넣을 수 있다.

다음과 같이 함수를 만들 때 매개변수는 없고 값만 반환하는 함수를 만들 수 있다.

return은 값을 반환하는 기능 뿐 아니라 함수 중간에서 바로 빠져나오는 기능도 있다. 다음은 매개변수 a 가 10이면 함수를 그냥 빠져나오는 함수이다.

인수로 10을 넣으면 return으로 함수 중간에서 빠져나와 그 아래 코드는 실행하지 않는다.

 

 29.4 함수에서 값을 여러 개 반환하기

함수에서 값을 여러 개 반환할 때는 다음과 같이 return에 값이나 변수를 콤마로 구분한다.

def 함수이름(매개변수):
    return 반환값1, 반환값2

다음은 두 수를 더한 값과 뺀 값을 반환하는 함수이다.

x의 값을 출력하면 두 값을 더한 30이 나오고, y의 값을 출력하면 두 값을 뺀 10이 나온다. return은 값을 여러 개 반환할 수 있다.

함수를 실행해 보면 다음과 같이 값은 튜플로 반환된다.

튜플을 언패킹 하여 두 변수에 할당한 것이다.

 

29.5 함수의 호출 과정 알아보기

스택은 아래에서 부터 차곡차곡 쌓고, 꺼낼때는 위쪽부터 차례대로 꺼내는 접시 쌓기와 비슷하다. 파이썬에서는 반대로 함수가 아래쪽 방향으로 추가되고, 함수가 끝나면 위쪽 방향으로 사라진다.

다음은 덧셈 함수 add와 곱셈 함수 mul이 있고, add함수 안에서 mul함수를 호출하는 소스코드이다.

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)

파이썬 스크립트가 실행되면 첫번째 줄부터 실행된다. 변수 y를 선언하는 부분까지 실행하면 전역 프레임에는 함수 mul, add와 변수 x,y가 들어간다.

프레임은 메모리에서 함수와 함수가 속한 변수가 저장되는 독립적인 공간이다. 전역 프레임은 파이썬 스크립트 전체에 접근할 수 있어서 전역 프레임 이라고 부른다.

함수 add를 호출한뒤 print(c)까지 보면 함수 add의 스택프레임이 만들어지고, 매개변수 a, b 와 변수 c 가 들어간다.

다음 줄에서는 함수 mul을 호출하여 함수 안으로 들어가 return c까지 실행하면 함수 mul의 스택프레임이 만들어지고, 매개변수 a,b와 변수 c가 들어가게 된다.

이제 add함수의 print(d)까지 실행하면 mul함수는 끝나며 반환값은 변수 d에 저장되며 add의 스택프레임에 저장된다. mul함수는 종료되었기 때문에 mul 스택프레임은 사라진다.

이후 add(x,y) 까지 실행하고 나면 add 함수도 끝나기 때문에 add의 스택 프레임도 사라지게 된다. 

함수는 스택 방향으로 호출되어 함수를 호출하면 스택의 아래쪽 방향으로 함수가 추가되고, 함수가 끝나면 위쪽 방향으로 사라진다. 전역프레임은 스크립트 파일의 실행이 끝나면 사라진다.

 

29.6 퀴즈

정답은 c이다.

정답은 d이다.

정답은 a,c,d이다.

 

29.3 연습문제: 몫과 나머지를 구하는 함수 만들기

정답은 다음 코드와 같다.

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

 

29.4 심사문제: 사칙연산 함수 만들기

정답은 다음 코드와 같다.

def calc(a,b):
    return a+b, a-b, a*b, a/b

 

Unit 30. 함수에서 위치 인수와 키워드 인수 사용하기

30.1 위치 인수와 리스트 언패킹 사용하기

함수에 인수를 순서대로 넣는 방식을 위치 인수라 한다. 다음은 print()함수에 인수들을 순서대로 넣은 것이다.

인수를 10,20,30 순서로 넣었으므로 출력할 때도 10,20,30의 순서로 출력된다.

 

30.1.1 위치 인수를 사용하는 함수를 만들고 호출하기

다음은 숫자 세개를 각 줄에 출력하는 함수이다.

print_numbers()에 세개의 숫자를 넣으면 각 줄에 숫자가 출력된다.

 

30.1.2 언패킹 사용하기

인수를 순서대로 넣을 때는 리스트나 튜플을 이용할 수 도 있다. 리스트나 튜플 앞에 *(애스터리스크)를 붙이면 언패킹이 된다. 

리스트(튜플)앞에 *을 붙이면 언패킹이 되어 print_numbers(10,20,30)과 똑같은 동작을 한다.

리스트 변수 대신 리스트 앞에 바로 *을 붙여도 된다.

언패킹을 사용하여 함수를 호출할때 매개변수의 개수와 리스트 요소의 개수는 반드시 같아야 한다. 만약 다르면 에러가 발생한다. 

 

30.1.3 가변인수 함수 만들기

가변 인수는 인수의 개수가 정해지지 않은 것이다. 같은 함수에 인수를 한 개를 넣을 수도 있고, 여러개를 넣을 수 도 있고, 넣지 않을 수도 있다. 

가변인수 함수는 다음과 같이 매개변수 앞에 *을 붙여서 만든다.

def 함수이름(*매개변수):
    코드

다음은 숫자 여러개를 입력 받고, 입력받은 숫자를 각 줄에 출력하는 함수이다.

매개 변수 이름은 원하는 대로 지어도 되지만 관례적으로 argumnets를 줄여서 args로 많이 사용한다. args는 튜플이라서 for 로 반복할 수 있다. 위와 같이 인수 여러개를 넣어도 되지만 리스트(튜플)을 언패킹 해도 된다.

고정인수와 가변인수를 함께 사용할 때는 다음과 같이 고정 매개변수를 먼저 지정하고, 그 다음 매개변수에 *을 지정해야 한다.

매개변수 순서에서 *args는 항상 가장 뒤쪽에 와야 한다.

 

30.2 키워드 인수 사용하기

함수에 인수를 넣을 때 값이나 변수를 그대로 넣게 되면 각각의 인수가 무슨 용도인지 알기 어려울 수 있다.

다음 함수의 경우 인수의 순서가 달라지면 잘못된 결과가 출력될 수 도 있다.

이러한 불편함을 해결하기 위해 인수의 순서와 용도를 매번 기억하지 않도록 키워드 인수 라는 기능을 제공한다. 키워드 인수는 키워드 = 값의 형식으로 인수에 키워드를 붙이는 기능이다. 

위 함수를 키워드 인수 방식으로 호출하면 다음과 같다.

키워드 인수를 사용하면 함수할 때 호출할 인수의 용도를 명확히 알 수 있고, 인수의 순서를 맞추지 않아도 키워드에 해당하는 값이 들어간다. print()함수의 sep, end도 키워드 함수이다.

 

30.3 키워드 인수와 딕셔너리 언패킹 사용하기

딕셔너리 언패킹은 딕셔너리 앞에 **(애스터리스크 두 개)를 사용한다.

personal_info 함수는 다음과 같다.

딕셔너리를 키워드:값 의 형식으로 만들고, **을 붙여서 함수에 출력하면 다음과 같이 딕셔너리의 값들이 함수의 인수로 들어간다. 딕셔너리의 키워드는 반드시 문자열 형태여야 한다.

딕셔너리 변수 대신 딕셔너리 바로 앞에 **을 붙여도 된다.

딕셔너리를 언패킹 할 때는 함수의 매개변수 이름과 딕셔너리의 키 이름이 같아야 하며 매개변수 개수와 딕셔너리의 키 개수가 같아야 한다. 만약 이름이나 개수가 다르면 에러가 발생한다.

 

30.3.1 **를 두 번 사용하는 이유 

딕셔너리는 키-값 쌍 형태로 값이 저장되어 있기 때문이다. *을 한번 만 사용하면 다음과 같이 딕셔너리의 키가 출력된다.

딕셔너리를 한 번 언패킹 하면 키를 사용하겠다는 뜻이 되고, 두번 언패킹하면 값을 사용하겠다는 뜻이 된다.

 

30.3.2 키워드 인수를 사용하는 가변 인수 함수 만들기 

키워드 인수를 사용하는 가변 인수 함수는 매개변수 앞에 **를 붙여서 만든다.

def 함수이름(**매개변수):
    코드

다음은 값을 여러개 받아서 매개변수 이름과 값을 각 줄에 출력하는 함수이다.

매개변수 이름은 아무거나 써도 되지만, 관례적으로 keyword arguments를 줄인 kwargs를 사용한다. 이 kwargs는 딕셔너리 이기 때문에 for문으로 반복할 수 있다.

위와 같이 원하는 만큼 값을 넣을 수 있다.

딕셔너리 언패킹을 사용하여 딕셔너리를 인수로 넣을 수도 있다.

함수를 만들때 매개변수에 **를 붙여주면 키워드 인수를 사용하는 가변인수 함수를 만들 수 있다. 함수를 호출 할 때는 키워드와 인수를 각각 넣거나, 딕셔너리 언패킹을 이용할 수 있다.

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

def personal_info(**kwargs):
    if 'name' in kwargs:    
        print('이름: ', kwargs['name'])
    if 'age' in kwargs:
        print('나이: ', kwargs['age'])
    if 'address' in kwargs:
        print('주소: ', kwargs['address'])

고정인수와 가변 키워드 인수를 같이 사용하려면 고정 인수를 먼저 지정하고 그 다음 매개변수에 **를 붙여야 한다.

위치인수를 받는 *args와 키워드 인수를 받는 **kwargs를 동시에 사용할 수 도 있다. 대표적인것이 print()함수 인데, 출력할 값을 위치 인수로 넣고, sep, end등을 키워드 인수로 넣는다.

 

30.4 매개변수에 초기값 지정하기

함수를 호출할 때 매개변수에 초기값을 지정하면 인수를 생략할 수 있다. 초기값은 다음과 같은 형식으로 지정한다.

def 함수이름(매개변수=값):
    코드

 

매개변수의 초기값은 주로 사용하는 값이 있으면서, 가끔 다른 값을 사용해야 할때 활용한다. print()의 경우 sep의 초기값은 공백으로 설정되어 있고, 가끔 sep에 다른 값들을 넣어 사용한다.

다음은 personal_info 함수의 address의 초기값을 비공개로 지정한 것이다.

address는 초기값이 있으므로 personal_info는 다음과 같이 address를 비워두고 호출할 수도 있고, 값을 넣으면 값이 전달된다.

 

30.4.1 초기값이 저장된 매개변수의 위치

초기값이 지정된 매개변수는 초기값이 없는 매개변수 앞에 올 수없다. 초기값이 지정된 매개변수가 중간에 나오면 인수가 어디로 들어가야 할 지 알 수 없기 때문에 에러를 발생한다. 초기값이 지정된 매개 변수는 항상 뒤쪽에 몰아 줘야 한다.

다음과 같이 모든 매개 변수에 초기값이 지정되면 인수를 넣지 않고도 호출할 수 있다.

def personal_info(name='비공개', age=0, address='비공개'):

 

30.5 퀴즈

정답은 d이다. 인수로 전달된것이 리스트이기 때문에 언패킹을 한 번만 해야 한다.

정답은 b,c이다.

정답은 a,c이다.

 

30.6 연습문제: 가장 높은 점수를 구하는 함수 만들기

정답은 다음 코드와 같다.

def get_max_score(*args):
    return max(args)

함수를 호출할 때 인수의 개수가 정해져 있지 않으므로 *args를 사용하고, 가장 높은 점수를 변수에 넣어 반환하므로 return을 사용해야 한다.

 

30.7 가장 낮은점수, 높은점수와 평균을 구하는 함수 만들기

정답은 다음 코드와 같다.

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

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

낮은 점수와 높은 점수는 min()함수와 max()함수를 이용하여 구했고, 평균은 키워드 인수를 사용했기 때문에 키워드의 값들만 더한후, 인수의 길이로 키의 개수로 나눠서 구했다.

 

Unit 31. 함수에서 재귀호출 사용하기

재귀호출은 함수 안에서 함수 자기 자신을 호출하는 방법이다. 알고리즘에 따라서 반복문으로 구현한 코드보다 재귀호출로 구현한 코드가 더 직관적이고 이해하기 쉬운 경우가 많다.

 

31.1 재귀호출 사용하기

def hello():
    print('Hello, world!')
    hello()
 
hello()

위 소스코드는 hello()라는 함수 안에서 다시 hello() 함수를 호출하고 있다. 소스코드를 실행하면 Hello, world! 가 계속 출력되다가 다음과 같은 에러가 발생한다.

파이썬에서는 최대 재귀 깊이가 1000으로 정해져있기 때문이다. hello함수가 자기 자신을 계속 호출하다가 최대 재귀 깊이를 넘어가면 RecursionError가 발생한다.

 

31.1.1 재귀 호출에 종료 조건 만들기

재귀 호출을 사용하려면 반드시 다음과 같이 종료 조건을 만들어야 한다.

def hello(count):
    if count == 0:
        return

    print('Hello, world!', count)
    count -= 1
    hello(count)
 
hello(3)

hello 함수의 반복 횟수를 계산하기 위해 매개변수 count를 사용해서 count 가 0이 되면 함수를 종료하고 0이 아니면 count를 1씩 감소시키면서 재귀 호출을 하는 코드이다. 

 

31.2 재귀 호출로 팩토리얼 구하기

팩토리얼은 1부터 n까지 양의 정수를 차례대로 곱한 값이며 ! 기호로 표시한다.

다음은 팩토리얼을 구하는 스크립트 코드이다. 

def factorial(n):
    if n == 1:
        return 1
    return n * factorial(n-1)

print(factorial(5))

1 팩토리얼은 1이기 때문에 n이 1이면 1을 반환하고 함수를 종료한다. 1이 아니면 n과 n에서 1을 뺀것을 factorial 함수에 넣어서 곱하는것을 재귀 호출로 반복한다. 함수에 5를 넣어 재귀 호출하는 것은 다음과 같다.

그리고 이것을 반환하며 연산하면 다음과 같다.

1부터 반환하여 1씩 증가하며 계속해서 곱한 값을 반환한다.

 

31.3 퀴즈 

정답은 a,c이다. 재귀 호출은 함수에서 자기 자신 함수를 호출하는 방식이며, 재귀호출은 반환값을 사용할 수 있다.

정답은 a이다.

정답은 24이다.

 

31.4 연습문제: 재귀 호출로 회문 판별하기

정답은 다음 코드와 같다.

    if len(word) < 2:
        return True
    if word[0] != word[-1]:
        return False
    return is_palindrome(word[1:-1])

첫번째 문자와 마지막 문자가 다르면 False를 반환하고 함수를 종료한다. 첫 번째 문자와 마지막 문자가 같으면 word[1:-1]로 첫번째 문자와 나머지 문자를 제거한 값을 재귀 호출한다. 인수로 받은 문자열이 2자 미만이 될때까지 재귀 호출이 완료되면 회문이기 때문에 True를 반환하고 함수를 종료한다.

 

31.5 심사문제: 재귀호출로 피보나치 수 구하기

정답은 다음 코드와 같다.

def fib(n):
    if n == 0:
        return 0
    elif n < 3:
        return 1
    else:
        return fib(n-1) + fib(n-2)

피보나치수는 n이 0이면 0이고, n이 1, 2일경우 1이다. 이외의 더 큰 숫자들은 n-1번째 수와 n-2번째 수의 합과 같다.

25.1.4 딕셔너리에서 키-값 쌍 삭제하기

pop(키)는 딕셔너리에서 해당 키-값 쌍을 삭제하고 삭제한 값을 반환한다.

pop(키, 기본값)처럼 기본값을 지정하면 딕셔너리에 키가 있다면 그 키에 해당하는 값을 반환하지만, 키가 없다면 기본값만 반환한다. 

del dic[key]의 형식으로도 키-값쌍을 삭제할 수 있다.

 

25.1.5 딕셔너리에서 임의의 키-값 쌍 삭제하기

popitem()은 딕셔너리에서 임의의 키-값쌍을 삭제하고 삭제한 키-값쌍을 튜플로 반환한다. 3.6이상 버전에서는 마지막 키-값쌍을 삭제하고 3.5 이하 버전에서는 무작위로 삭제하여 매번 삭제하는 키-값쌍이 달라진다.

 

25.1.6 딕셔너리의 모든 키-값 쌍 삭제하기

clear()로 딕셔너리의 모든 키-값쌍을 삭제하여 빈 딕셔너리로 만들 수 있다.

 

25.1.7 딕셔너리에서 키의 값을 가져오기

get(키)는 딕셔너리에서 지정한 키에 해당하는 값을 가져온다.

get(키,기본값)의 형태로 사용하면 키가 있을 경우 키의 값을 반환하지만, 키가 없을 경우 기본값을 반환한다.

 

25.1.8 딕셔너리에서 키-값 쌍을 모두 가져오기

딕셔너리에서 키와 값을 가져오는 메서드는 다음과 같다.

  • items : 키-값쌍을 모두 가져옴
  • keys : 키를 모두 가져옴
  • values : 값을 모두 가져옴

 

25.1.9 리스트와 튜플로 딕셔너리 만들기

먼저 키로 사용할 값들이 들어있는 리스트나 튜플을 준비해야 한다. 그리고 dict.fromkeys()에 키가 들어있는 리스트를 넣으면 딕셔너리가 생성된다. 

dict.fromkeys(키리스트)의 형식으로 딕셔너리를 생성하면 값들은 모두 None가 되고, dict.fromkeys(키리스트, 값)의 형식으로 딕셔너리를 생성하면 값들은 모두 지정한 값이 된다.

 

defaultdict 사용하기

딕셔너리는 없는 키에 접근하면 에러를 발생한다. collections 모듈의 defaultdict를 사용하면 없는 키에 접근하더라도 에러를 발생하지 않고 기본값을 반환한다. defaultdicr()괄호 안에는 기본값 생성 함수가 들어간다.

int()에 아무 값도 넣지 않고 호출하면 0이 나오기 때문에 int 의 기본값으로 0이 나온다.

다른 값을 기본값으로 넣고 싶으면 다음과 같이 사용하면 된다. 

 

25.2 반복문으로 딕셔너리의 키-값 쌍을 모두 출력하기

다음과 같이 for 반복문에 딕셔너리를 지정한 후에 i를 출력하면 키값만 출력된다.

값까지 같이 출력하려면 다음과 같이 for in 뒤에 딕셔너리를 지정하고 items를 사용해야 한다.

키와 값 순서대로 키는 k에 지정하고, 값은 v에 지정하여 출력할 수 있다. 

for in 뒤에 딕셔너리 변수가 아니라 딕셔너리를 직접 지정해도 된다.

 

25.2.1 딕셔너리 키만 출력하기

keys()를 사용하면 된다.

 

25.2.2 딕셔너리의 값만 출력하기

values()를 사용하면 된다.

 

25.3 딕셔너리 표현식 사용하기

딕셔너리도 for 반복문과 if조건문을 이용하여 딕셔너리를 생성할 수 있다. 사용 방법은 다음과 같다.

{키:값 for 키, 값 in 딕셔너리}
dict({키:값 for 키, 값 in 딕셔너리})

for in 다음에 딕셔너리를 지정하여 items()로 키,값 모두를 가져와서 딕셔너리를 생성한다.

다음과 같이 keys()로 키만 가져와 특정 값을 넣거나, values()로 값만 가져와 키로 사용하거나, 키와 값의 자리를 바꿀 수도 있다.

 

25.3.1 딕셔너리 표현식에서 if 조건문 사용하기

딕셔너리는 특정 키를 삭제하는 메서드만 제공하고, 특정 값을 찾아서 삭제하는 메서드는 제공하지 않는다. 반복문을 통해 삭제하려고 하는 값을 찾으면 키를 통해 삭제하려는 방식으로 코드를 구성하면 삭제하고 나서도 반복을 진행해야 하기 때문에 딕셔너리 크기가 중간에 바뀌어서 에러가 발생하게 된다.

이와 같은 경우 딕셔너리 표현식에서 if 조건문을 사용하여 삭제할 값을 제외할 수 있다.

{키: 값 for 키, 값 in 딕셔너리 if 조건식}
dict({키: 값 for 키, 값 in 딕셔너리 if 조건식})

위와 같은 방식은 키-값 쌍을 삭제하는 것이 아니라 삭제할 키-값 쌍을 제외하고 새로운 딕셔너리를 만드는 것이다.

 

25.4 딕셔너리 안에서 딕셔너리 사용하기

딕셔너리는 다음과 같이 값 부분에 다시 딕셔너리가 계속 들어갈 수 있다.

딕셔너리 = {키1: {키A: 값:A}, 키2: {키B: 값B}}

다음은 지구형 행성의 반지름, 질량, 공전주기를 딕셔너리로 표현한 예시이다.

terrestrial_planet = {
    'Mercury': {
        'mean_radius': 2439.7,
        'mass': 3.3022E+23,
        'orbital_period': 87.969
    },
    'Venus': {
        'mean_radius': 6051.8,
        'mass': 4.8676E+24,
        'orbital_period': 224.70069,
    },
    'Earth': {
        'mean_radius': 6371.0,
        'mass': 5.97219E+24,
        'orbital_period': 365.25641,
    },
    'Mars': {
        'mean_radius': 3389.5,
        'mass': 6.4185E+23,
        'orbital_period': 686.9600,
    }
}
 
print(terrestrial_planet['Venus']['mean_radius'])  

딕셔너리 안에 있는 딕셔너리에 접근하려면 딕셔너리 뒤에 [ ](대괄호)를 단계만큼 붙이고 키를 지정해주면 된다.

딕셔너리[키][키]

 

25.5 딕셔너리의 할당과 복사

딕셔너리를 다른 변수에 할당하면 두 딕셔너리는 이름만 다를 뿐 같은 객체이다.

같은 객체이기 때문에 한 변수의 키-값을 변경하면 둘 다 반영된다. 

x와 y를 다른 두 딕셔너리로 만드려면 copy()메서드를 사용하여 모든 키-값 쌍을 복사하면 된다.

x와 y는 다른 객체이기 때문에 is로 비교하면 false가 나오며 둘 중 한 딕셔너리의 값을 변경해도 다른 딕셔너리에 반영되지 않는다.

 

25.5.1 중첩 딕셔너리의 할당과 복사 알아보기

중첩 딕셔너리는 copy메서드로 복사해도 한 딕셔너리의 값 변경시 다른 딕셔너리에도 적용된다. 
중첩 딕셔너리를 완전히 복사하려면 copy모듈의 deepcopy함수를 사용해야 한다.

 

25.6 퀴즈

정답은 c,e이다.

정답은 b,c이다. setdefault는 키만 지정하면 값은 None가 되고, keys는 키만 가져온다.

정답은 b,c,e 이다.

정답은 d 이다.

정답은 d이다.

정답은 c이다.

 

25.7 연습문제: 평균 점수 구하기

정답은 다음 코드와 같다.

average = sum(maria.values()) / len(maria)

 

25.8 심사문제: 딕셔너리에서 특정 값 삭제하기

정답은 다음 코드와 같다.

x = {key: value for key, value in x.items() if value != 30 and key != 'delta'}

삭제해야 하는 요소 중에 해당하는 값을 사용하는 경우도 있기 때문에 표현식을 사용해야 한다.

 

Unit 26. 세트 사용하기

파이썬에서는 집합을 표현하는 세트 자료형을 제공한다. 세트는 합집합, 교집합, 차집합 등의 연산이 가능하다.

 

26.1 세트 만들기

세트는 { }(중괄호) 안에 값을 저장하고, 콤마로 구분한다.

세트는 요소의 순서가 정해져 있지 않고, 요소가 중복되지 않는다. 같은 값을 두 개 이상 넣는다면, 하나만 저장된다. 요소의 순서가 정해져 있지 않기 때문에 인덱스로 특정 요소를 출력할 수 없다.

 

26.1.1 세트에 특정 값이 있는지 확인하기

in 연산자를 사용하면 된다. 값이 있으면 True, 없으면 False가 나온다. 

 

in 앞에 not을 붙이면 값이 없는지 확인한다.

 

26.1.2 set를 사용하여 세트 만들기

set(반복 가능한 객체)와 같은 형식으로 세트를 만든다. 반복 가능한 객체에서 중복된 요소는 제거된다.

빈 세트는 set에 아무것도 지정하지 않으면 된다.

세트는 세트 안에 세트를 넣을 수 없다.

프로즌세트 라는 내용을 변경할 수 없는 세트도 있다. 

프로즌세트는 요소를 추가, 삭제 하는 연산 메서드를 사용할 수 없고, 요소를 변경할 수 없다.

프로즌세트는 다음과 같이 프로즌세트를 중첩해서 넣을 수 있다.

 

26.2 집합 연산 사용하기

| 연산자와 set.union()메서드는 합집합을 구한다. 

& 연산자와 set.intersection()메서드는 교집합을 구한다.

-연산자와 set.difference()메서드는 차집합을 구한다.

^연산자와 set.symmetric_difference()메서드는 대칭 차집합을 구한다. 대칭 차집합은 xor연산자의 특성을 그대로 따라 두 집합중 겹치지 않는 요소만 포함한다.

 

26.2.1 집합 연산 후 할당 연산자 사용하기

세트 자료형에 |,&,-,^연산자와 할당연산자 =을 함께 사용하면 집합 연산의 결과를 다시 변수에 할당한다.

|= 은 현재 세트에 다른 세트를 더하며 update()메서드와 같은 동작을 한다.

&= 와 intersection_update() 메서드는 현재 세트와 다른 세트 중 겹치는 요소만 저장한다.

-= 와 difference_update 메서드는 현재 세트에서 다른 세트를 뺀다.

^= 와 symmetric_difference_update()메서드는 현재 세트와 다른 세트중 겹치지 않는 요소만 현재 세트에 저장한다.

 

26.2.2 부분 집합과 상위 집합

세트는 부분집합, 진부분집합, 상위집합, 진상위집합과 같이 속하는 관계를 표현할 수 있다. 

<=와 issubset() 메서드는 현재 세트가 다른 세트의 부분집합인지 확인한다.

<은 현재 세트가 다른 세트와 진부분집합인지 확인한다. 같은 기능을 하는 메서드는 없다. 진부분집합은 부분집합이지만 두 집합이 같지 않을때 참이다.

>=와 issuperset()메서드는 현재 세트가 다른 세트의 상위 집합인지 확인한다. 두 집합이 같을때도 참이다.

> 은 현재 세트가 다른 세트의 진상위집합인지 확인한다. 같은 기능을 하는 메서드는 없다. 진상위집합은 상위집합이지만 두 집합이 같지 않을 때 참이다.

 

26.2.3 세트가 같은지 다른지 확인하기

== 연산자를 이용하여 세트가 서로 같은지 확인할 수 있다. 세트의 요소는 순서가 없으므로 요소만 같으면 참이다.

!= 연산자는 세트가 다른지 확인한다.

 

26.2.4 세트가 겹치지 않는지 확인하기

disjoint()는 현재 세트가 다른 세트와 겹치지 않는지 확인한다. 겹치는 요소가 없으면 True, 있으면 False를 반환한다.

 

26.3 세트 조작하기

26.3.1 세트에 요소 추가하기

add(요소)는 세트에 요소를 추가한다.

 

26.3.2 세트에서 특정 요소를 삭제하기

remoce(요소)는 세트에서 특정 요소를 삭제하고, 요소가 없으면 에러를 발생한다.

discard(요소)는 세트에서 특정 요소를 삭제하고, 요소가 없으면 그냥 넘어간다.

 

26.3.3 세트에서 임의의 요소 삭제하기

pop()는 세트에서 임의의 요소를 삭제하고 해당 요소를 반환한다. 만약 요소가 없으면 에러를 발생한다.

 

26.3.4 세트의 모든 요소 삭제하기

clear()로 세트의 모든 요소를 삭제할 수  있다.

 

26.3.5 세트의 요소 개수 구하기

len(세트)로 세트의 요소 개수를 구할 수 있다.

 

26.4 세트의 할당과 복사

세트를 만든뒤 다른 변수에 할당하면 이 두 변수는 같은 세트를 가리키기 때문에 is 연산자로 비교해보면 True가 나오고, 요소를 추가하면 두 세트 모두 반영된다.

세트 a와 b를 완전히 두 개로 만드려면 copy()메서드로 모든 요소를 복사해야 한다.

복사를 하면 완전히 다른 두 객체이기 때문에 is 연산자로 비교하면 False가 나오며 값을 추가해도 다른 세트에 반영되지 않는다.

 

26.5 반복문으로 세트의 요소를 모두 출력하기

간단하게 for in 뒤에 세트만 지정하면 된다.

세트의 요소는 순서가 없기 때문에 숫자가 아닌 세트는 출력할 때 마다 순서가 달라질 수 있다.

다음과 같이 in 다음에 세트를 직접 지정해도 된다.

 

26.6 세트 표현식 사용하기

다음과 같이 세트 안에 for 반복문을 지정하면 세트를 생성할 수 있다.

{식 for 변수 in 반복가능한객체}
set(식 for 변수 in 반복가능한객체)

문자열 apple에서 중복되지 않은 문자인 a,p,l,e를 꺼내서 세트를 만든다.

 

26.6.1 세트 표현식에 if 조건문 사용하기

다음과 같이 if 조건문은 for 반복문 뒤에 지정한다.

{식 for 변수 in 세트 if 조건식}
set(식 for 변수 in 세트 if 조건식)

if 조건문을 지정하면 if조건문에서 특정 요소를 제외한 뒤 세트를 생성한다.

 

26.7 퀴즈

정답은 b이다. b는 빈 딕셔너리를 생성하는 방식이다.

정답은 b,e이다. a ^ b는 {1,2,4,5} 이고,  e는 {4,5}이다.

정답은 b,d이다. 진부분집합을 구하는 메서드는 없고, 진상위집합은 두 세트가 같으면 거짓이다.

정답은 a,b,c 이다.

정답은 c,d이다. symmetric_difference_update는  ^=와 같고, issupperset는 >=와 같다.

정답은 a,c이다.

 

26.8 연습문제: 공배수 구하기

정답은 다음 코드와 같다.

{i for i in range(1,101) if i % 3 == 0}
{i for i in range(1,101) if i % 5 == 0}

1부터 100까지 range()로 만들고 그 중 3의배수인 것과 5의 배수인 것을 각 세트에 넣는다.

 

26.9 심사문제: 공약수 구하기

정답은 다음 코드와 같다.

a, b = map(int, input().split())
a = {i for i in range(1, a+1) if a % i == 0}
b = {i for i in range(1, b+1) if b % i == 0}

a, b를 정수로 입력 받고, 1 부터 입력 받은 수까지 입력받은 수와 나눴을때 나머지가 0이 되는 숫자들을 각 세트에 저장했다.

 

+ Recent posts