Focus not on constructing a data collection but rather on describing "what" that data collection consists of.
Encapsulation
One obvious way of focusing more on "what" than "how" is simply to refactor code, and to put the data construction in a more isolated place-i.e., in a function or method
#configure the data to start with
collection = get_initial_state()
state_var = None
for datum in data_set:
if condition(state_var):
state_var = calculate_from(datum)
new = modify(datum, state_var)
collection.add_to(new)
else:
new = modify_differently(datum)
collection.add_to(new)
# Now actually work with the data
for thing in collection:
process(thing)
changed programming logic, nor even the lines of code to shift the focus from "How do we construct collection?" to "What does make_collection() create?"
# truck away construction of data
def make_collection(data_set):
collection = get_initial_state()
state_var = None
for datum in data_set:
if condition(state_var):
state_var = calculate_from(datum, state_var)
new = modify(datum, state_var)
collection.add_to(new)
else:
new = modify_differently(datum)
collection.add_to(datum)
return collection
# Now actually work with the data
for thing in make_collection(data_set):
process(thing)
Comprehensions
A comprehension is an expression that uses the same keywords as loop and conditional blocks, but inverts their order to focus on the data rather than on the procedure.
collection = list()
for datum in data_set:
if condition(datum):
collection.append(datum)
else:
new = modify(datum)
collection.append(now)
more compactly
collection = [d if condition(d) else modify(d) for d in data_set]
Generators
본체 안에 yield 키워드를 가진 함수는 모두 제너레이터 함수. 일반 함수와 제너레이터 함수의 차이는 yield 키워드를 사용한다는 구문 차이 밖에 없음
제너레이터 함수는 함수 본체를 포함하는 제너레이터 객체를 생성함. .next()를 제너레이터 객체에 호출하면 함수 안에 있는 다음 yield를 진행하며, .next()는 함수가 중단된 곳에서 생성된 값을 평가
마지막으로 함수가 반환될 때, 이 함수를 포함하고 있는 제너레이터 객체는 Iterator 프로토콜에 따라 Stop Iteration 예외를 발생시킴
Generator comprehensions are merely a description of "how to get the data" that is not realized until one explictly asks for it, either by cally .next() on object, or by looping over it. This often saves memory for large sequences and defers computation until it is actually needed.
long_lines = (line for in read_line(huge_log_file) if complex_condition(line))
For typical uses, the behavior is the same as if you havd constructed a list, but runtime behavior is nicer.
def get_long_lines(log_file):
line = read_line(log_file)
while True:
try:
if complex_condition(line):
yield line
line = read_line(log_file)
except StopIteration:
raise
log_lines = get_log_lines(huge_log_file)
In fact, even using yield is somewhat of an abstaction from the underlying "iterator protocol". We could do this with a class that had .next() and .iter() methods.
class GetLogLines(object):
def __init__(self, log_file):
self.log_file = log_file
self.line = None
def __iter__(self):
return self
def __next__(self):
if self.line is None:
self.line = read_line(log_file)
while not complex_condition(self.line):
self.line = read_line(self.log_file)
return self.line
log_lines = GetLogLines(huge_log_file)
Recursion
- using recursion effectively as a way of marching through a sequence of elements is, while possible, really not "Pythonic"
- Python is simpy comparatively slow at recursion, and has a limited stack depth limit
Python lacks an internal feature called tail call elination that makes deep recursion comptuationally efficient in some languages.def running_sum(numbers, start=0): if len(numbers) == 0: print() return total = numbers[0] + start print(total, end=" ") running_sum(numbers[1:], total)
'공부하는삶 > Python' 카테고리의 다른 글
[TIL] FastAPI 응답과 오류 처리 HTTPException (0) | 2023.09.25 |
---|---|
Python numpy, tensorflow, pytorch array (0) | 2023.08.29 |
Python Composite pattern(composition), Descriptor, Meta class (0) | 2023.08.29 |
Sequence (0) | 2020.05.09 |
Magic Method (0) | 2020.05.09 |
Gaussian Filter vs Bilateral Filter (0) | 2020.04.02 |