A Byte of Python - More Python


More

지금까지 파이썬의 주요 다양한 기능을 살펴봤다. 이 챕터에서는 파이썬에 대한 지식을 더 깊게하는 몇가지를 살펴볼 것이다.

Passing tuples around

함수로 부터 다른 두 개의 값을 반환할 수 있다. 튜플을 사용하면 된다.

>>> def get_error_details():
... return (2,'details')
...
>>> errnum, errstr = get_error_details()
>>> errnum
2
>>> errstr
'details'

a, b = < some expression > 는 표현식의 결과값이 2개로된 튜플형식으로 해석된다.

이는 또한 파이썬에서 두 값을 스왑하는 가장 빠른 방법은 다음임을 나타낸다.
>>> a = 5; b=8
>>> a, b
(5, 8)
>>> a, b = b, a
>>> a, b
(8, 5)

특별한 메소드들

__init__, __del__ 메소드와 같은 몇가지의 특별한 메소드가 있다. 

특별한 메소드들은 빌트인 형식의 기본동작을 모방하는데 사용되곤 한다. 예를들어, 클래스에 대해 인덱싱 연산 x[key] 을 사용한다고하면 __getitem__() 메소드를 사용하는 것이다. list클래스 자체에서 파이썬이 하는 방식이다.

다음 테이블에 유용한 특별한 메소드를 나열했다. 만약 모든 특별한 메소드를 알고 싶으면 메뉴얼을 살펴본다.

- __init__(self, ...)
: 이 메소드는 새롭게 생성된 객체가 사용을 위해 반환되기 전에 호출된다.
- __del__(self)
: 이 메소드는 객체가 파괴되기 전에 호출된다. (이는 예측되지 않는 타이밍에 실행되므로 사용하는 것을 피한다.)
- __str__(self)
: print함수나 str()이 사용될 때 호출됨
- __lt__(self, other)
: 보다 작다 연산자(<) 가 사용됐을 때 호출된다. 비슷하게, 모든 연산자에 특별한 메소드가 있다 (+, > 등)
- __getitem__(self, key)
: x[key] 인덱싱 연산이 사용됐을 때 호출된다.
- __len__(self)
: 빌트인 len() 함수가 시퀀스 객체에 대해 호출되었을 때 사용된다.

단일 문장 블럭

문장의 블럭은 인덴트 레벨로 구별됨을 보았다. 만약 단일 문장만 있다면 같은 라인에 쓸 수 있다. 설명을 위해 다음 예시를 보자.

>>> flag = True
>>> if flag: print("Yes")
...
Yes

이 단일 문장은 분리된 블럭이 아닌 함꼐 쓰여질 수 있다. 비록, 이게 프로그램을 작게 만들지만, 에러 체킹을 제외하고 이 숏컷 메소드를 사용하지 않는 것을 추천한다. 인덴트를 넣어야 추가적인 식을 넣는게 쉽기 때문이다.

람다 형식

람다 식은 새로운 함수 객체를 생성할 때 사용되곤 한다. 특히, 람다는 단일 표현식으로 파라미터를 취한다. 람다는 함수의 몸체가 된다. 이 표현식의 값은 새로운 함수에서의 반환갑시다.

more_lambda.py

points = [{'x':2,'y':3},{'x':4,'y':1}]
points.sort(key=lambda i:i['y'])
print(points)


Output:

$ python more_lambda.py
[{'y': 1, 'x': 4}, {'y': 3, 'x': 2}]

How It Works

list의 sort 메소드는 key파라미터를 받아 어떻게 리스트를 정렬할지를 가늠한다. 이 경우, 사용자 소트라서 함수를 작성해야 한다. 함수를 위한 분리된 def 블럭 대신 이 장소에만 사용되는 함수이므로 람다 연산식을 사용한다.

리스트 콤프레헨션

리스트 콤프레헨션은 존재하는 리스트에서 새로운 리스트를 파생할 때 사용되곤 한다. 숫자 리스트를 가지고 모든 숫자에 대해 2보다 큰 것이면 2배를 한다고 해보자. 리스트 콤프레헨션은 이런 상황을 위한 것이다.

[표현식 for 요소 in 컬렉션 [if 조건식]]


more_list_comprehension.py

listone=[2,3,4]
listtwo=[2*i for i in listone if i > 2]
print(listtwo)

Output:

$ python more_list_comprehension.py
[6, 8]


How It Works

Here, we derive a new list by specifying the manipulation to be done (2*i) when some condition is satisfied (if i > 2). Note that the original list remains unmodified.

The advantage of using list comprehensions is that it reduces the amount of boilerplate code required when we use loops to process each element of a list and store it in a new list.

함수에서 튜플과 딕셔너리 받기

*또는 ** 프리픽스를 각각 사용해 튜플이나 딕셔너리로 함수의 파라미터를 받는 특별한 방법이 있다. 이는 함수에서 매개변수의 가변 숫자를 받을 때 유용하다.

>>> def powersum(power, *args):
... '''Return the sum of each argument raised to the specified power.'''
... total = 0
... for i in args:
...    total+=pow(i,power)
... return total
...
>>> powersum(2,3,4)
25
>>> powersum(2,10)
100

args변수에 * 를 지정해 함수에 전달되는 추가 매개변수는 튜플로 처리된다. 만약 ** 를 사용했다면 추가 매개변수는 연관된 키/값 딕셔너리가된다.

assert 문

assert문은 어떤 것이 참임을 단언할 때 사용한다. 예를 들어, 리스트의 최소한 하나의 엘리먼트를 가지는게 당연하고 그렇지 않으면 에러가 발생해야 할 때 assert를 사용한다. assert가 실패할 때 AssertionError가 발생한다. pop()메소드는 리스트에서 마지막 아이템을 제거하고 반환한다.

>>> mylist = ['item']
>>> assert len(mylist) >= 1
>>> mylist.pop()
'item'
>>> assert len(mylist) >= 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AssertionError
The assert statement should be used judiciously. Most of the time, it is better to catch exceptions, either handle the problem or display an error message to the user and then quit.

Decorators

데코레이터는 래퍼함수에 적용되는 숏컷이다. 이 것은 같은 코드가 계속 반복되는 기능을 감쌀때 유용하다. 예를 들어, 자체적으로 사용할 retry 데코레이터를 생성(실행중에 예외가 발생하면 다시 수행하는, 최대 5번, 재시작 사이에 딜레이)하면 어떤 함수든지 적용할 수 있다. 이 것은 특히 원격 컨트롤러를 호출하는 네트워크를 만드는 상황에서 유용하다.

from time import sleep
from functools import wraps
import logging
logging.basicConfig()
log = logging.getLogger("retry")

def retry(f):
  @wraps(f)
  def wrapper_function(*args,**kwargs):
    MAX_ATTEMPTS = 5
    for attempt in range(1, MAX_ATTEMPTS+1):
      try:
        return f(*args, **kwargs)
      except Exception:
        log.exception("Attempt %s/%s failed: %s", attempt, MAX_ATTEMPTS, (args, kwargs))
        sleep(10* attempt)
    log.critical("All %s attempts failed: %s", MAX_ATTEMPTS, (args, kwargs))
  return wrapper_function

counter = 0

@retry
def save_to_database(arg):
  print("Write to a database or make a network call or etc.")
  print("This will be automatically retried if exception is thrown.")
  global counter
  counter+=1
  if counter < 2:
    raise ValueError(arg)

if __name__ == '__main__':
  save_to_database("Some bad value")

Output:

$ python more_decorator.py
Write to a database or make a network call or etc.
This will be automatically retried if exception is thrown.
ERROR:retry:Attempt 1/5 failed : (('Some bad value',), {})
Traceback (most recent call last):
  File "more_decorator.py", line 14, in wrapper_function
    return f(*args, **kwargs)
  File "more_decorator.py", line 39, in save_to_database
    raise ValueError(arg)
ValueError: Some bad value
Write to a database or make a network call or etc.
This will be automatically retried if exception is thrown.


How It Works

See:

Video : Python Decorators Made Easy

파이썬 2와 3의 차잇점

See:

"Six" library
Porting to Python 3 Redux by Armin
Python 3 experience by PyDanny
Official Django Guide to Porting to Python 3
Discussion on What are the advantages to python 3.x?

Summary

We have covered some more features of Python in this chapter and yet we haven't covered all the features of Python. However, at this stage, we have covered most of what you are ever going to use in practice. This is sufficient for you to get started with whatever programs you are going to create.

Next, we will discuss how to explore Python further.


덧글

댓글 입력 영역