TIL (2020.05.30)

Awesome·2020년 5월 30일
0

TIL

목록 보기
6/46

Python

List/Dictionary comprehension

List(or dictionary) comprehension(리스트 혹은 딕셔너리 표현식) 은 새로운 리스트 혹은 딕셔너리를 만들 때 반복문과 조건문을 활용하여 간단하게 작성할 수 있는 표현식이다.
예를 들어 1~10 사이에서 짝수만 출력하는 코드를 작성해보자.

even_num = []
for i in range(1,11):
    if i % 2 == 0:
        even_num.append(i)

print(even_num)

> [2, 4, 6, 8, 10]

위의 코드를 list comprehension으로 표현해보자.

even_num = [i for i in range(1,11) if i % 2 == 0]

print(even_num)

> [2, 4, 6, 8, 10]

단 한줄로 작성이 가능하다. 이처럼 리스트 표현식은 코드의 길이를 단축시켜주는 효과가 있다.

[ 표현식 for 변수 in 반복가능객체 if 조건식 ]

과 같은 방식으로 표현할 수 있다. 변수가 여러 개일 경우, for ~ if문까지를 뒤에 이어서 반복하면 된다.

[ 표현식 for 변수1 in list_1 if 조건식1
for 변수2 in list_2 if 조건식2 ....
for 변수n in list_n if 조건식n ]

딕셔너리의 경우 아래와 같이 표현할 수 있다.

population_of_city = [('Tokyo', 36923000), ('Shanghai', 34000000), ('Jakarta', 30000000), ('Seoul', 25514000), ('Guangzhou', 25000000), ('Beijing', 24900000), ('Karachi', 24300000),('Shenzhen', 23300000),('Delhi', 21753486)]

new_dic = {item[0]:item[1] for item in population_of_city}

print({key:value for key,value in new_dic.items() if value > 30000000})

> {'Tokyo': 36923000, 'Shanghai': 34000000}

이번엔 성능 차이를 확인해보겠다.

import timeit

def for_loop():
    num_list = []
    for i in range(1000):
        num_list.append(i)

def list_comprehension():
    num_list = [i for i in range(1000)]

if __name__ == "__main__":
    time1 = timeit.Timer("for_loop()", "from __main__ import for_loop")
    print("for loop time = ", time1.timeit(number=1000), "milliseconds")

    time2 = timeit.Timer("list_comprehension()", "from __main__ import list_comprehension")
    print("list_comprehension time = ", time2.timeit(number=1000), "milliseconds")

> for loop time =  0.060382699 milliseconds
> list_comprehension time =  0.029650842999999996 milliseconds

리스트 표현식이 일반적인 반복문보다 약 2배가 빠르다.

이렇게 따져보니, 반복문을 쓸 필요가 없어보인다. 리스트 표현식이 속도도 빠르고, 코드도 짧아서 더 개발자스러워 보이고 간지도 난다. 하지만 이것은 흔한 비개발자 출신들의 겉 멋든 착각이다. 갓 구글에서는 파이썬 가이드에서 아래와 같이 리스트 표현식 사용에 대해 권고하고 있다.

일단 첫 줄부터 simple case에 사용하라고 하고 있다.
단점을 보면 복잡한 표현식은 가독성을 해친다고 되어 있다.

내가 풀스텍 디지털 노마드 코더가 되서 와이키키 해변에 앉아 혼자 코딩을 하며 살지 않는 이상, 언제나 누군가와 협업해야하고, 내 코드를 공유해야 한다. 나 혼자 잘났다고 반복문이나 조건문이 4, 5개 이상 달린 리스트 표현식을 써놓으면 협업하는 이들은 그 코드를 파악하고 해석하기 위해 일반적인 구문보다 더 많은 시간을 할애해야 한다. 이러한 시간들이 쌓여서 결국엔 업무 비효율이 높아지고 팀워크를 해친다.

여러가지 상황을 고려하여 가장 효율적인 방법을 찾아낼 수 있는 사람이 찐 고수다.


Class의 속성과 static, class method

클래스에 대해서는 간단하게 파악이 되었다.

  • 속성이나 method를 정의할 때, 항상 self를 parameter로 받는다.
  • self 를 통해서 method 가 인스턴스의 parameter에 접근 가능해진다. 예시 : obj.method(a,b)
  • 클래스의 속성을 정의하기 위해서는 __init__ method 안에 self.속성으로 할당한다.
  • 인스턴스를 생성한다. 이 때, self는 인스턴스 자신이다.
class wecode:
    def __init__(self):
        self.name="wecode"
        self.place="wework"
        self.floor=10

    def impression(self):
        print(f"{self.name}는 겁나 빡세다.")
         
me = wecode()
me.impression()
 
> wecode는 겁나 빡세다.

같은 결과지만 인스턴스에서 값을 받아 전달하게 만들 수도 있다.

class wecode:
    def __init__(self, name, place, floor):
        self.name=name
        self.place=place
        self.floor=floor

    def impression(self):
        print(f"{self.name}는 겁나 빡세다.")
         
me = wecode("위코드","위워크",10)
me.impression()
 
> wecode는 겁나 빡세다.

Class 속성

지금부터는 본격적으로 class의 속성에 대해 더 알아보겠다.

속성은 크게 두 가지로 나눌 수 있다.

  1. 클래스 속성
  2. 인스턴스 속성

말 그대로, 클래스 속성은 클래스 전반에 걸쳐 공통적으로 적용되는 속성이다. 반면 인스턴스 속성은 각각의 객체에 제한적으로 적용되는 속성이다.
말 보다는 예시로 보자.

class Bag:
    bag_inside = []
    
    def put_something(self, stuff):
        Bag.bag_inside.append(stuff)

iu = Bag()
iu.put_something("마이크")

bibi = Bag()
bibi.put_something("신발")

print("iu 가방에 있는 것:",iu.bag_inside)
print("bibi 가방에 있는 것:",bibi.bag_inside)

> iu 가방에 있는 것: ['마이크', '신발']
> bibi 가방에 있는 것: ['마이크', '신발']

가방이라는 클래스 안에 아이유랑 비비가 각각 마이크와 신발을 넣었다. 결과를 보니 아이유와 비비의 가방에 있는 물건이 똑같다. 그 이유는 클래스 전체에서 가방이 공유되고 있기 때문이다.
반면, 인스턴스 속성을 이용하면 두 사람의 가방 속 물건이 다르다.

class Bag:
    def __init__(self):
        self.bag_inside = []
    
    def put_something(self, stuff):
        self.bag_inside.append(stuff)

iu = Bag()
iu.put_something("마이크")

bibi = Bag()
bibi.put_something("신발")

print("iu 가방에 있는 것:",iu.bag_inside)
print("bibi 가방에 있는 것:",bibi.bag_inside)

> iu 가방에 있는 것: ['마이크']
> bibi 가방에 있는 것: ['신발']

인스턴스 별로 가방을 분리해서 사용한다. 따라서 각각 따로 저장되는 것을 볼 수 있다.

비공개 속성

클래스 안에서만 접근가능하고, 외부에서는 접근하지 못하도록 속성을 비공개로 설정할 수 있다.

__속성 = 값 으로 설정하면 외부에서 접근 불가능함

class Bag:
     __bag_limit = 10
     
x = Bag()
print(x.__bag_limit)   

> AttributeError: 'Bag' object has no attribute '__bag_limit'

static method 와 class method

Static method

Static method 는 인스턴스를 생성하지 않고 사용하며, 따라서 객체의 상태에 영향을 받지 않는다. 즉, class 가 아닌 오로지 parameter 만 신경쓰면 된다.

class Dates:
    @staticmethod
    def toDashDate(date):
        return date.replace("/", "-")

dateFromDB = "15/12/2016"
dateWithDash = Dates.toDashDate(dateFromDB)
print(dateWithDash)

> 15-12-2016

위와 같이 static method를 사용하면 인스턴스를 생성하지 않고 parameter 만 할당하면 결과를 얻을 수 있다.

그럼 언제 쓰나?

솔직히 이걸 왜 쓰나 이해가 안되서 찾아보니 파이썬 조상 중에 한 명인 루시아노 하말류라는 양반이 이렇게 말했다고 한다.

@classmethod 는 쓰임새가 많은 게 확실하지만, @staticmethod 는 사용해야 하는 이유를 잘 모르겠다.
클래스와 함게 작동하지 않는 함수를 정의하려면, 단지 함수를 모듈에 정의하면 된다. 아마 함수가 클래스를 건드리지는 않지만, 그 클래스와 밀접히 연관되어 있어서 클래스 코드 가까운 곳에 두고 싶을 수는 있을 것이다. 그런 경우에는 클래스의 바로 앞이나 뒤에서 함수를 정의하면 된다.

저 양반도 왜 쓰는지 모른단다. 이 논쟁으로 레오나르도 로챌라는 양반이랑 싸웠단다. 마치 예전에 양자역학으로 보어랑 아인슈타인이 싸운 것 마냥 나름 고수들끼리는 자기 소신과 고집이 있어서 이런 논쟁이 빈번한가보다.
난 아마 직접 경험해보면서 깨달아야 할 것 같다.

Class method

인스턴스를 생성하지 않는다는 점에서 static method 와 같지만, class 의 속성과 method에 접근한다는 점에서 차이점이 발생한다.
Class 를 만들때 항상 self 가 첫 번째 parameter로 오는 것과 같이 class method 도 cls(class)가 첫 번째 parameter 로 온다.
아까 가방을 예시로 다시 들어보자.

class Bag:
    bag_inside = []
    count = 0
    
    def put_in_bag(self, *stuff):
        Bag.bag_inside += [item for item in stuff]
        Bag.count = len(Bag.bag_inside)
        
    @classmethod
    def bag_info(cls):
        print(f"가방에 {cls.count}개의 물건이 들어있다.")
        print(f"가방에 있는 물건들은 {cls.bag_inside} 이다.")
        
iu = Bag()
iu.put_in_bag("마이크","신발","앨범")
bibi = Bag()
bibi.put_in_bag("과자","우산")

Bag.bag_info()

> 가방에 5개의 물건이 들어있다.
> 가방에 있는 물건들은 ['마이크', '신발', '앨범', '과자', '우산'] 이다.

이처럼 bag_inside와 count 두 class 속성을 받아 인스턴스 생성 없이 classmethod 를 통해서 결과를 출력하였다.

추가로, classmethod는 class 의 상속관계를 반영한다.

class Class1:
    sentence = "We are in Wework1"
    
    @classmethod
    def printing(cls):
        print(cls.sentence)
    
class Class2(Class1):
    sentence = "We are in Wework2"
    
Class2.printing()

> We are in Wework2

Class2 는 Class1의 printing method를 상속 받아, 위와 같이 정상적인 결과를 출력하였다. 그 이유는 classmethod에 정의된 cls parameter가 상위 class 를 인자로 받기 때문이다.

최종적으로 정리해보면 staticmethod 와 classmethod는 다음과 같은 특징이 있다.

  • staticmethod와 classmethod 모두 인스턴스에는 독립적이지만 class 에는 종속된다.
  • staticmethod는 class의 속성이나 변수에 접근 불가하다.
  • 반면 classmethod는 클래스를 의미하는 cls parameter를 통해 class 자체를 인자로 받으므로 접근 가능하다.
  • classmethod는 class 내의 상속을 반영할 수 있다.
profile
keep calm and carry on

0개의 댓글