Chap02 Working With Data
1. 변수
1. 같은 값의 변수는 다중 할당을 사용해라
파이썬은 다중 할당을 지원한다. 간결하게 값을 정의할 수 있다.
🙅♀️
x = 'foo'
y = 'foo'
z = 'foo'
🙆♀️
x = y = z = 'foo'
2. 변수 값 스와핑 할 때 임시변수를 사용하지 마라.
파이썬에선 튜플을 사용하면 좋다
🙅♀️
foo = 'Foo'
bar = 'Bar'
temp = foo
foo = bar
bar = temp
🙆♀️
foo = 'Foo'
bar = 'Bar'
(foo, bar) = (bar, foo)
2. Strings
1.체인을 통한 간단한 string 변경하기
일부 데이터에 대한 변형을 연속적으로 할 때, 호출을 연결하는 것이 임시 변수를 사용하는 것 보다 훨씬 간결합니다. 너무많은 연결은 좋지 않고, 3개정도가 좋습니다.
🙅♀️
book_info = ' The Three Musketeers: Alexandre Dumas'
formatted_book_info = book_info.strip()
formatted_book_info = formatted_book_info.upper()
formatted_book_info = formatted_book_info.replace(':', ' by')
🙆♀️
book_info = ' The Three Musketeers: Alexandre Dumas'
formatted_book_info = book_info.strip().upper().replace(':', ' by')
2. ‘‘.join 사용해 리스트값 단순한 문자열 만들기
’‘.join 을 사용하면 더 빠르고 메모리도 덜 사용할 수 있다. ‘’ 안에는 합칠 때 추가하고싶은 기호를 쓰면 추가가 된다.
🙅♀️
result_list = ['True', 'False', 'File not found']
result_string = ''
for result in result_list:
result_string += result
🙆♀️
result_list = ['True', 'False', 'File not found']
result_string = ''.join(result_list)
>>> result_string
'TrueFalseFile not found'
result_string = ','.join(result_list)
>>> result_string
'True,False,File not found'
3. ord 사용하여 아스키코드 쉽게 쓰기
문자를 ascii 코드로 변환하거나, ascii코드를 문자로 변환할 경우 ord() 함수를 사용하면 간편하다.
🙅♀️
hash_value = 0
character_hash = {
'a': 97, 'b': 98, 'c': 99, # ... 'y': 121, 'z': 122,
}
some_string = "abcabc"
for e in some_string:
hash_value += character_hash[e]
hash_value
🙆♀️
hash_value = 0
some_string = "abcabc"
for e in some_string:
hash_value += ord(e)
hash_value
4. format string 을 위한 함수를 선호
문자열을 포맷하는 방법
- 하드코딩 문자열과 변수가 혼합된 문자열 생성
+를 이용한 문자열 변수 결함- % 를 이용한 방법
문자열 형식을 지정하기 위해 format 함수를 을 사용하는게 제일 좋다. 간결하며, 이름이 지정된 자리 표시자를 사용하고, 문자열 너비를 제어할 수 있다.
🙅♀️
def get_formatted_user_info_worst(user):
# 변환 오류가 발생하기 쉽다.
return 'Name: ' + user.name + ', Age: ' + \
str(user.age) + ', Sex: ' + user.sex
def get_formatted_user_info_slightly_better(user):
# 타입을 알 필요 없다.
# 문자열과 자리표시자의 구분이 없다.
return 'Name: %s, Age: %i, Sex: %c' % (user.name, user.age, user.sex)
🙆♀️
def get_formatted_user_info(user):
# 간결하다. 어디가 포맷하는 곳인지 알 수 있다.
return 'Name: {user.name}, Age: {user.age}, Sex: {user.sex}'.format(user=user)
3. Lists
1. comprehension 을 사용한 기존 목록 변형하기
기존 데이터를 변형할 때 comprehension 을 사용하면 코드의 명확성이 높아진다. cPy
thon을 사용하기 때문에 성능 또한 높아진다.
🙅♀️
some_other_list = range(10)
some_list = list()
for element in some_other_list:
if is_prime(element):
some_list.append(element + 5)
🙆♀️
some_other_list = range(10)
some_list = [element + 5
for element in some_other_list
if is_prime(element)
]
2. 음수 인덱스 사용하기
음수 인덱스를 사용하면 목록의 끝에서 시작해서 거꾸로 계산된다.
🙅♀️
def get_suffix(word):
word_length = len(word) return word[word_length - 2:]
🙆♀️
def get_suffix(word):
return word[-2:]
3. map, filter 보다 comprehension 사용하기
python에서 사용하는 map, filter외에 comprehension 사용이 훨씬 간결하고 읽기 쉽다.
🙅♀️
the_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
def is_odd(number):
return number % 2 == 1 odd_numbers = filter(is_odd, the_list)
odd_numbers_times_two = list(map(lambda x: x * 2, odd_numbers))
🙆♀️
the_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
odd_numbers_times_two = [n * 2 for n in the_list if n % 2 == 1]
4. built-in 함수 사용하기
초보 개발자들은 sum 을 놓치는 경우가 많다. 내장 함수가 있다면 내장 함수를 사용하는 것이 가장 좋다.
🙅♀️
the_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
the_sum = 0
for element in the_list:
the_sum += element
🙆♀️
the_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
the_sum = sum(the_list)
5. all()을 이용해 iterable 요소가 true인지 확인하기
sum 과 마찬가지로 all 또한 개발 초보들이 놓치는 함수. iterable 요소가 모두 true 면 true를 반환해준다.
🙅♀️
def contains_zero(iterable):
for e in iterable:
if e == 0: return True
return False
🙆♀️
def contains_zero(iterable):
# 0 is "Falsy," so this works
return not all(iterable)
6. `` 연산자를 이용하여 나머지 나타내기
함수에 대한 인자를 다룰 때 * 연산자를 사용해 시퀀스의 나머지 부분을 나타낼 수 있다.
🙅♀️
some_list = ['a', 'b', 'c', 'd', 'e']
(first, second, rest) = some_list[0], some_list[1], some_list[2:]
print(rest)
(first, middle, last) = some_list[0], some_list[1:-1], some_list[-1]
print(middle)
(head, penultimate, last) = some_list[:-2], some_list[-2], some_list[-1]
print(head)
🙆♀️
some_list = ['a', 'b', 'c', 'd', 'e']
(first, second, *rest) = some_list
print(rest)
(first, *middle, last) = some_list
print(middle)
(*head, penultimate, last) =
some_list
print(head)
4.Dictionaries
1. Switch…case 대신 dict사용하기
python 에는 switch..case 구문이 없다. if else 를 사용하는건 느리고, dict를 통해 해결할 수 있다.
🙅♀️
def apply_operation(left_operand, right_operand, operator):
if operator == '+':
return left_operand + right_operand
elif operator == '-':
return left_operand - right_operand
elif operator == '*':
return left_operand * right_operand
elif operator == '/':
return left_operand / right_operand
🙆♀️
def apply_operation(left_operand, right_operand, operator):
import operator as op
operator_mapper = {
'+': op.add,
'-': op.sub,
'*': op.mul,
'/': op.truediv
}
return operator_mapper[operator](left_operand, right_operand)
2. dict.get 사용해 default value 적용하기
🙅♀️
log_severity = None
if 'severity' in configuration:
log_severity = configuration['severity']
else:
log_severity = 'Info'
🙆♀️
log_severity = configuration.get('severity', 'Info')
3. dict comprehenstion 사용해서 명확성 높이기
list comprehension 을 사용하는것 처럼 dict comprehension 을 사용할 수 있다.
🙅♀️
user_email = {}
for user in users_list:
if user.email:
user_email[user.name] = user.email
🙆♀️
user_email = {user.name: user.email
for user in users_list if user.email}
2.5 Sets
1. set operation의 이해
set은 dict처럼 {}를 사용하지만, dict과 달리 비슷하지만, value 가 없다.
set class는 interable, container를 가지고있어 for loop 사용이 가능하다.
합집합: A, B, 또는 A 및 B 모두의 요소를 결합합니다 (A | B)
교집합: A와 B 모두에 있는 이러한 요소의 교차점 (A & B)
차집합 가 아닌 A에서 요소 간의 차이점. (A - B)
참고: 여기에서 순서가 중요합니다. A - B는 반드시 B - A와 동일하지 않습니다.
대칭 차이: A ^ B
데이터 목록에서, 스퀀스 기반으로 요소를 찾을 수 있다.
🙅♀️
def get_both_popular_and_active_users():
# Assume the following two functions each return a
# list of user names
most_popular_users = get_list_of_most_popular_users()
most_active_users = get_list_of_most_active_users()
popular_and_active_users = []
for user in most_active_users:
if user in most_popular_users:
popular_and_active_users.append(user)
🙆♀️
def get_both_popular_and_active_users():
# Assume the following two functions each return a # list of user names
return(set(get_list_of_most_active_users()) & set(get_list_of_most_popular_users()))
2. set comprehension 을 정확하게 사용하기
set comprehension을 사용하면 간결하게 나타낼 수 있다.
🙅♀️
users_first_names = set()
for user in users:
users_first_names.add(user.first_name)
🙆♀️
users_first_names = {user.first_name for user in users}
3. set을 사용하여 중복값 제거하기
중복된 값을 목록 중 고유한 값을 필요로 할 때 set을 사용.
- 값에 고유한 요소만 존재
- 존재하는 요소가 들어올 경우 무시
🙅♀️
unique_surnames = []
for surname in employee_surnames:
if surname not in unique_surnames:
unique_surnames.append(surname)
def display(elements, output_format='html'):
if output_format == 'std_out':
for element in elements:
print(element)
elif output_format == 'html':
as_html = '<ul>'
for element in elements:
as_html += '<li>{}</li>'.format(element)
return as_html + '</ul>'
else:
raise RuntimeError('Unknown format {}'.format(output_format))
🙆♀️
unique_surnames = set(employee_surnames)
def display(elements, output_format='html'):
if output_format == 'std_out':
for element in elements:
print(element)
elif output_format == 'html': as_html = '<ul>'
for element in elements:
as_html += '<li>{}</li>'.format(element)
return as_html + '</ul>'
else:
raise RuntimeError('Unknown format {}'.format(output_format))
2. 튜플
>>> t1 = ()
>>> t2 = (1,)
>>> t3 = (1, 2, 3)
>>> t4 = 1, 2, 3
>>> t5 = ('a', 'b', ('ab', 'cd'))
1. collections.namedtuple 을 사용해 많은 튜플들을 더 깔끔하게 사용하기
튜플 - 논리적으로 배열된 데이터를 사용
데이터베이스 테이블 단일 행 반환과 같은 목록으로 사용한다. namedtuple을 사용하면 인덱스가 아닌 이름으로 필드에 액세스 할 수 있다.
가독성과 유지보수성을 높일 수 있다.
🙅♀️
def print_employee_information(db_connection):
db_cursor = db_connection.cursor()
results = db_cursor.execute('select * from employees').fetchall()
for row in results:
print(row[1] + ', ' + row[0] + ' was hired '
'on ' + row[5] + ' (for $' + row[4] + ' per annum)'
'into the ' + row[2] + ' department and reports to ' + row[3])
🙆♀️
from collections import namedtuple
EmployeeRow = namedtuple('EmployeeRow', ['first_name', 'last_name', 'department', 'manager', 'salary', 'hire_date'])
EMPLOYEE_INFO_FMT = '{last_name}, {first_name} was hired on \
{hire_date} (for ${salary} per annum) into the {department} \
department and reports to {manager}'
def print_employee_information(db_connection):
db_cursor = db_connection.cursor()
results = db_cursor.execute('select * from employees').fetchall()
for row in results:
employee = EmployeeRow._make(row)
print(EMPLOYEE_INFO_FMT.format(**employee._asdict()))
2. 무시해야하는 튜플에는 _사용하기
튜플을 데이터와 매칭할 때, 보통 모든 데이터를 필요로 하지 않는다. 이때 사용하지 않는 데이터에는 _를 붙혀서 이 데이터는 사용하지 않는다고 알려주는것이 좋다.
🙅♀️
(name, age, temp, temp2) = get_user_info(user)
if age > 21:
output = '{name} can drink!'.format(name=name)
# "Wait, where are temp and temp2 being used?"
🙆♀️
(name, age, _, _) = get_user_info(user)
if age > 21:
output = '{name} can drink!'.format(name=name)
3. 튜플을 사용하여 압축 풀기
하나하나 풀지 않고 (a, b, c)로 한번에 풀 수 있다.
🙅♀️
list_from_comma_separated_value_file = ['dog', 'Fido', 10]
animal = list_from_comma_separated_value_file[0]
name = list_from_comma_separated_value_file[1]
age = list_from_comma_separated_value_file[2]
output = ('{name} the {animal} is {age} years old'.format(
animal=animal, name=name, age=age))
🙆♀️
list_from_comma_separated_value_file = ['dog', 'Fido', 10]
(animal, name, age) = list_from_comma_separated_value_file
output = ('{name} the {animal} is {age} years old'.format(
animal=animal, name=name, age=age))
4. 튜플을 사용하여 다양한 변수값 리턴하기
함수에서 여러값을 반환하는게 필요할때 이상적으로 사용할 수 있다.
🙅♀️
from collections import Counter
STATS_FORMAT = """Statistics:
Mean: {mean}
Median: {median}
Mode: {mode}"""
def calculate_mean(value_list):
return float(sum(value_list) / len(value_list))
def calculate_median(value_list):
return value_list[int(len(value_list) / 2)]
def calculate_mode(value_list):
return Counter(value_list).most_common(1)[0][0]
values = [10, 20, 20, 30]
mean = calculate_mean(values)
median = calculate_median(values)
mode = calculate_mode(values)
print(STATS_FORMAT.format(mean=mean, median=median,
mode=mode))
🙆♀️
from collections import Counter
STATS_FORMAT = """Statistics:
Mean: {mean}
Median: {median}
Mode: {mode}"""
def calculate_staistics(value_list):
mean = float(sum(value_list) / len(value_list))
median = value_list[int(len(value_list) / 2)]
mode = Counter(value_list).most_common(1)[0][0]
return (mean, median, mode)
(mean, median, mode) = calculate_staistics([10, 20, 20, 30])
print(STATS_FORMAT.format(mean=mean, median=median, mode=mode))
7. classes
1. isinstance 함수를 사용하여 객체 타입 결정
파이썬은 유형을 지정하지 않지만 내부적으로 정해지기 때문에 타입 에러가 발생할 수 있다.
isinstance를 이용해 변수 타입이 달라질 때마다 작동방식을 변경할 수 있다.
🙅♀️
def get_size(some_object):
"""Return the "size" of *some_object*, where size = len(some_object) for sequences, size = some_object for integers and floats, and size = 1 for True, False, or None."""
try:
return len(some_object)
except TypeError:
if some_object in (True, False, type(None)):
return 1
else:
return int(some_object)
print(get_size('hello'))
print(get_size([1, 2, 3, 4, 5]))
print(get_size(10.0))
🙆♀️
def get_size(some_object):
if isinstance(some_object, (list, dict, str, tuple)):
return len(some_object)
elif isinstance(some_object, (bool, type(None))):
return 1
elif isinstance(some_object, (int, float)):
return int(some_object)
print(get_size('hello'))
print(get_size([1, 2, 3, 4, 5]))
print(get_size(10.0))
2. private를 사용할땐 _ 를 쓰기
파이썬에서 모든 속성은 public이지만, priviate value로 사용할 경우 의도를 명확하게 하기위해 `__ 를 붙힌다.
- 클라이언트가 사용하지 않는
protected값은_하나를 사용한다. - 서브클래스가 접근할 수 없는
private속성은__두개를 사용한다.
🙅♀️
class Foo():
def __init__(self):
self.id = 8
self.value = self.get_value()
def get_value(self):
pass
def should_destroy_earth(self):
return self.id == 42
class Baz(Foo):
def get_value(self, some_new_parameter):
pass
class Qux(Foo):
def __init__(self):
super(Qux, self).__init__()
self.id = 42
q = Qux()
b = Baz() # Raises 'TypeError'
q.should_destroy_earth() # returns True
q.id == 42 # returns True
🙆♀️
class Foo():
def __init__(self):
self.__id = 8
self.value = self.__get_value() # Our 'private copy'
def get_value(self):
pass
def should_destroy_earth(self):
return self.__id == 42
class Baz(Foo):
def get_value(self, some_new_parameter):
pass
class Qux(Foo):
def __init__(self):
self.id = 42
# No relation to Foo's id, purely coincidental
super(Qux, self).__init__()
q = Qux()
b = Baz()
with pytest.raises(AttributeError):
getattr(q, '__id')
3. @property 를 사용하여 직접 클래스 구현에 대한 대비
@property 를 사용하여 데이터 액세스를 대비할 수 있다. @property 를 사용함으로써 미래 여러값들 getter, setter 값을 다르게 적용하여 사용할 수 있다.
🙅♀️
class Product():
def __init__(self, name, price):
self.name = name
# We could try to apply the tax rate here, but the object's price
# may be modified later, which erases the tax
self.price = price
🙆♀️
class Product():
def __init__(self, name, price):
self.name = name
self._price = price
@property
def price(self):
# now if we need to change how price is calculated, we can do it
# here (or in the "setter" and __init__)
return self._price * TAX_RATE
@price.setter
def price(self, value):
# The "setter" function must have the same name as the property
self._price = value
🤔4 기계가 읽을 수 있는 클래스를 위해 __repr__ 사용하기
__str__ 은 사람이 읽을 수 있는 방식으로 클래스를 사용하지만__repr__ 는 기계가 사용할수 있는 방식으로 사용된다. 객체를 재구성하는데 필요한 모든 정보가 포함되어야 하며 이걸 통해 다른 인스턴스를 구분할 수 있다.
🙅♀️
class Foo():
def __init__(self, bar=10, baz=12, cache=None):
self.bar = bar
self.baz = baz
self._cache = cache or {}
def __str__(self):
return 'Bar is {}, Baz is {}'.format(self.bar, self.baz)
def log_to_console(instance):
print(instance)
log_to_console([Foo(), Foo(cache={'x': 'y'})])
🙆♀️
class Foo():
def __init__(self, bar=10, baz=12, cache=None):
self.bar = bar
self.baz = baz
self._cache = cache or {}
def __str__(self):
return '{}, {}'.format(self.bar, self.baz)
def __repr__(self):
return 'Foo({}, {}, {})'.format(self.bar, self.baz, self._cache)
def log_to_console(instance):
print(instance)
log_to_console([Foo(), Foo(cache={'x': 'y'})])
??어렵다
사용자한테 필요 없어repr에만 넣은건가?
기계는 컴퓨터 디바이스를 말하는건가
5. 객체 출력시 __str__ 를 이용하여 사람들이 읽을수 있게 반환
인스턴스를 print로 출력하면 인스턴스 아이디 값이 나오지만, __str__ 를 사용해 사람들이 읽을 수 있게 변환할 수 있다.
🙅♀️
class Point():
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(1, 2)
print(p)
🙆♀️
class Point():
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return '{0}, {1}'.format(self.x, self.y)
p = Point(1, 2)
print(p)
# Prints '1, 2'
8. Context Managers
1. 컨텍스트 관리자 사용하기
컨텍스트 매니저
원하는 타이밍에 정확하게 리소스를 할당하고 제공하는 역할
리소스 관리를 안전하고 명확하게 만들 수 있다. 컨텍스트 관리자를 사용하지 않으면 에러가 발생했을 때 열린 파일을 닫을 수 없다.
🙅♀️
file_handle = open(path_to_file, 'r')
for line in file_handle.readlines():
if raise_exception(line):
print('No! An Exception!')
🙆♀️
with open(path_to_file, 'r') as file_handle:
for line in file_handle:
if raise_exception(line):
print('No! An Exception!')
9. Genorators
1. 간단한 이터레이션에 generator expression 사용하기
리스트 컴프리헨션을 사용하면 모든 데이터를 메모리에 적재시키므로, 하나씩 올리는 제너레이터를 사용하자
🙅♀️
for uppercase_name in [name.upper() for name in get_all_usernames()]:
process_normalized_username(uppercase_name)
🙆♀️
for uppercase_name in (name.upper() for name in get_all_usernames()):
process_normalized_username(uppercase_name)
2. 제너레이터를 사용한 레이지로드
한번에 처리하는게 계산이 많이 거릴것 같은 경우에는 이터레이터를 반환하는 제너레이터를 사용해서 호출을 중단처리를 통해 효율을 높이는게 좋은듯?
🙅♀️
def get_twitter_stream_for_keyword(keyword):
imaginary_twitter_api = ImaginaryTwitterAPI()
if imaginary_twitter_api.can_get_stream_data(keyword):
return imaginary_twitter_api.get_stream(keyword)
current_stream = get_twitter_stream_for_keyword('#jeffknupp')
for tweet in current_stream:
process_tweet(tweet)
def get_list_of_incredibly_complex_calculation_results(data):
return [first_incredibly_long_calculation(data),
second_incredibly_long_calculation(data),
third_incredibly_long_calculation(data),
]
🙆♀️
def get_twitter_stream_for_keyword(keyword):
imaginary_twitter_api = ImaginaryTwitterAPI()
while imaginary_twitter_api.can_get_stream_data(keyword):
yield imaginary_twitter_api.get_stream(keyword)
for tweet in get_twitter_stream_for_keyword('#jeffknupp'):
if got_stop_signal:
break
process_tweet(tweet)
def get_list_of_incredibly_complex_calculation_results(data):
yield first_incredibly_long_calculation(data)
yield second_incredibly_long_calculation(data)
yield third_incredibly_long_calculation(data)