Python is the most popular programming language largely owing to its pseudo-code like syntax.
It has some mighty powerful features and the fairly easy learning curve makes it the first choice language for developers all over the globe.
While list comprehensions are still the shiniest tool, destructuring is where the power lies.
Destructuring assignments are a shorthand syntax for mapping one or more variables from the left-hand side to the right-hand side counterparts. It’s handy in unpacking values as we shall see shortly.
Swapping And Updating Two Or More Values
Interchanging underlying values of variables is the most basic form of destructuring and it works in the following way:
a, b = b, a
Though at a high level it may seem that both the statementsa = bandb = aare only running simultaneously, there’s actually more going on under the hood.
Essentially, the right-hand side is evaluated first, convertingb,ainto a tuple. Subsequently, the tuple is unpacked and assigned to the left-hand side in the order left to right.
Consider the following snippet that updates the values ofaandb.
a = 1
b = 2
a, b = a + b, a
Itwon’tgive you a value 3 for bothaandb. Instead, the value ofbafter the assignment would be 1 whileabecomes 3.
The destructuring assignment allows us to swap more than two variables as well:
a,b,c = c,a,b
Unpacking Tuples And Iterables
Now, from what we’ve explored in the previous section, multiple assignments are fairly straightforward in Python. We can do it in a few ways:
a = b = c = 10
a, b = 2, 3
The latter one is a destructuring assignment that works by creating a tuple of the right-hand side.
So, taking a cue from it we can opt to unpack values by using a tuple on the left-hand side as shown below:
a, b = (2, 3)
(a, b) = 2, 3
xy = 1, 2
a, b = xy
Destructuring assignment isn’t just restricted to tuples. In fact, you could use it on lists, strings, and pretty much any iterable.
a, b = [2,3]
names = "How are you?"
a,b,c = names.split()
Using destructuring to split a string is really powerful but you need to ensure that the number of values on the left side must exactly match the number of values on the right side to avoidValueError
Recursive unpacking to access nested elements
We can unpack nested elements from lists or tuples with the same destructuring assignment syntax.
(a, b) = ([1, 2, 3], [1, 2])
([a, b], c) = ([1, 2], 3)
[a, b] = [[1,2],[3,4]]
Trailing comma
Trailing commas are optional for two or more element tuples. In case you’re using a single element tuple or you need to unpack only one element you can use the trailing comma in the following ways:
x = (1) #prints 1
x = (1,) #prints (1,)
a, = 1,
a, = [1]
[a] = [1]
#all print a == 1
a, = [1,2]
#ValueError too many elements
While parentheses are just syntactic sugar, it’s the comma that actually tells Python that the data type is a tuple. At times trailing comma can be missed which makes[a] = myLista better bet in terms of readability.
Access Elements Without Indices
Another place where the destructuring assignment is incredibly useful is when accessing values from a list. Without destructuring, more often than not, you’d end up accessing elements using indexes which can be needless.
By using the destructuring syntax in theenumeratefunction below, we’re able to access elements in a way that’s more concise and easy to comprehend.
myList = ["a", "b", "c"]
for i, element in enumerate(myList):
print(counter, element)
By following the same approach we can destructure dictionaries into key-value pairs or a list of tuples as shown below.
listOfTuples = [(1,0),(0,1)]
for i, (p1, p2) in enumerate(listOfTuples):
#do your magic here
Select Or Omit Certain Values
By using the * operator (introduced in Python 3), we can choose to pick an optional unknown number of elements as shown below:
*a, b = 1, 2, 3, 4
# a is [1,2,3]
#b is 4
The asterisk operator creates a new list of all but the last element and puts them ina. This is useful when you need to extract sublists in Python.
We can also use the asterisk operator for getting the first and last elements from a list or even packing values and pass them into a function — which helps us get rid of long parameter lists in functions.
def sum(a, b, c):
return a + b + c
x = (1, 2, 3)
print(result(*x))
Note: Dictionary unpacking requires**.
On the contrary, if you’re looking to drop or ignore the value in destructuring assignments using the_syntax is the way to go.*_lets you omit an unknown number of values.
a, _ = [1, 2]
first, *_, last = "Hello"
#first is 'H'
#last is 'o'
Conclusion
So, we saw how destructuring syntax helps in swapping and multi-assignments as well as in unpacking tuples, iterable and accessing elements without indexes in a for a loop.
I hope you make good use of the destructuring syntax in Python.
This is a quick one about how to configure Python’s build in logging system using an ini file. Our application needs to import logging and logging.config to be able to use ini files.
클래스로 매개변수와 반환값을 처리하는 데코레이터를 만들 때는 __call__ 메서드에 매개변수를 지정하고, self.func에 매개변수를 넣어서 호출한 뒤에 반환값을 반환해주면 됩니다. 여기서는 매개변수를 *args, **kwargs로 지정했으므로 self.func에 넣을 때는 언패킹하여 넣어줍니다.
물론 가변 인수를 사용하지 않고, 고정된 매개변수를 사용할 때는 def __call__(self, a, b):처럼 만들어도 됩니다.
42.5.1클래스로 매개변수가 있는 데코레이터 만들기
마지막으로 매개변수가 있는 데코레이터를 만들어보겠습니다. 다음은 함수의 반환값이 특정 수의 배수인지 확인하는 데코레이터입니다.
decorator_class_parameter.py
classIsMultiple:def__init__(self,x):# 데코레이터가 사용할 매개변수를 초깃값으로 받음self.x=x# 매개변수를 속성 x에 저장def__call__(self,func):# 호출할 함수를 매개변수로 받음defwrapper(a,b):# 호출할 함수의 매개변수와 똑같이 지정(가변 인수로 작성해도 됨)r=func(a,b)# func를 호출하고 반환값을 변수에 저장ifr%self.x==0:# func의 반환값이 self.x의 배수인지 확인print('{0}의 반환값은 {1}의 배수입니다.'.format(func.__name__,self.x))else:print('{0}의 반환값은 {1}의 배수가 아닙니다.'.format(func.__name__,self.x))returnr# func의 반환값을 반환returnwrapper# wrapper 함수 반환@IsMultiple(3)# 데코레이터(인수)defadd(a,b):returna+bprint(add(10,20))print(add(2,5))
실행 결과
add의 반환값은 3의 배수입니다.
30
add의 반환값은 3의 배수가 아닙니다.
7
먼저 __init__ 메서드에서 데코레이터가 사용할 매개변수를 초깃값으로 받습니다. 그리고 매개변수를 __call__ 메서드에서 사용할 수 있도록 속성에 저장합니다.
def__init__(self,x):# 데코레이터가 사용할 매개변수를 초깃값으로 받음self.x=x# 매개변수를 속성 x에 저장
지금까지 __init__에서 호출할 함수를 매개변수로 받았는데 여기서는 데코레이터가 사용할 매개변수를 받는다는 점 꼭 기억해두세요.
이제 __call__ 메서드에서는 호출할 함수를 매개변수로 받습니다. 그리고 __call__ 메서드 안에서 wrapper 함수를 만들어줍니다. 이때 wrapper 함수의 매개변수는 호출할 함수의 매개변수와 똑같이 지정해줍니다(가변 인수로 작성해도 됨).
def__call__(self,func):# 호출할 함수를 매개변수로 받음defwrapper(a,b):# 호출할 함수의 매개변수와 똑같이 지정(가변 인수로 작성해도 됨)
wrapper 함수 안에서는 func의 반환값이 데코레이터 매개변수 x의 배수인지 확인합니다. 이때 데코레이터 매개변수 x는 속성에 저장되어 있으므로 self.x와 같이 사용해야 합니다. 그리고 배수 확인이 끝났으면 func의 반환값을 반환합니다. 마지막으로 wrapper 함수를 다 만들었으면 return으로 wrapper 함수를 반환합니다.
def__call__(self,func):# 호출할 함수를 매개변수로 받음defwrapper(a,b):# 호출할 함수의 매개변수와 똑같이 지정(가변 인수로 작성해도 됨)r=func(a,b)# func를 호출하고 반환값을 변수에 저장ifr%self.x==0:# func의 반환값이 self.x의 배수인지 확인print('{0}의 반환값은 {1}의 배수입니다.'.format(func.__name__,self.x))else:print('{0}의 반환값은 {1}의 배수가 아닙니다.'.format(func.__name__,self.x))returnr# func의 반환값을 반환returnwrapper# wrapper 함수 반환
데코레이터를 사용할 때는 데코레이터에 ( )(괄호)를 붙인 뒤 인수를 넣어주면 됩니다.
@데코레이터(인수)def함수이름():코드
@IsMultiple(3)# 데코레이터(인수)defadd(a,b):returna+b
지금까지 데코레이터를 사용하는 방법을 배웠는데 문법이 조금 복잡했습니다. 여기서는 데코레이터가 기존 함수를 수정하지 않으면서 추가 기능을 구현할 때 사용한다는 점만 기억하면 됩니다. 보통 데코레이터는 프로그램의 버그를 찾는 디버깅, 함수의 성능 측정, 함수 실행 전에 데이터 확인 등에 활용합니다(앞에서 만든 함수의 시작과 끝을 출력하는 데코레이터, 매개변수와 반환값을 출력하는 데코레이터는 디버깅에 활용할 수 있습니다. 그리고 함수 실행 전에 데이터를 확인하는 예제는 연습문제에서 소개하겠습니다).