Better way 38 ๊ฐ๋จํ ์ธํฐํ์ด์ค์ ๊ฒฝ์ฐ ํด๋์ค ๋์ ํจ์๋ฅผ ๋ฐ์๋ผ
ํ (hook)
- Python ๋ด์ฅ API ์ฌ์ฉํ ๋, ํจ์๋ฅผ ์ ๋ฌํ์ฌ ์คํํ๋ ๊ฒฝ์ฐ
- Python์ ํจ์๋ฅผ ์ผ๊ธ ์๋ฏผ ๊ฐ์ฒด๋ก ์ทจ๊ธํ๊ธฐ์ ๊ฐ๋ฅ
์ผ๊ธ ์๋ฏผ (first-class citizen)
- ์๋ฌด๋ฐ ์ ์ฝ ์์ด ์ฌ์ฉํ ์ ์๋ ๋ฐ์ดํฐ ๊ฐ
- ํจ์ ์ธ์๋ก ๋๊ธฐ๊ธฐ ๊ฐ๋ฅ
- ํจ์ ๋ฐํ ๊ฐ์ผ๋ก ์ฌ์ฉ ๊ฐ๋ฅ
- ๋ณ์๋ ๋ฐ์ดํฐ ๊ตฌ์กฐ์ ์ ์ฅ ๊ฐ๋ฅ
languages = ['python', 'swift', 'java']
languages.sort(key=len) # hook
print(languages)
>>>
['java', 'swift', 'python']
๋์ ๋๋ฆฌ์ ํค๊ฐ ์ถ๊ฐ ์ฌ๋ถ๋ฅผ ์ถ์ ํ๋ ๋ด์ฉ์ด ํ์ํ๋ค๊ณ ํ์.
defaultdict ์ธ์์ log_missing ์ ๊ตฌํํ์ฌ, ํค ์ถ๊ฐ๋ฅผ ์ถ์ ํ ์ ์๋ค.
def log_missing():
print('ํค ์ถ๊ฐ') # ํค๊ฐ ์ถ๊ฐ๋๋ฉด ์ถ๋ ฅ
return 0 # ๊ธฐ๋ณธ๊ฐ 0
from collections import defaultdict
colors = {'์ด๋ก': 1, 'ํ๋': 2} # ๊ธฐ์กด
increments = [('์ด๋ก',3), ('๋นจ๊ฐ',4), ('๋
ธ๋',5)] # ์
๋ฐ์ดํธ ๋ด์ฉ
result = defaultdict(log_missing, colors)
print(f'์ด์ : {dict(result)}')
for color, count in increments:
result[color] += count
print(f'์ดํ : {dict(result)}')
>>>
์ด์ : {'์ด๋ก': 1, 'ํ๋': 2}
ํค ์ถ๊ฐ
ํค ์ถ๊ฐ
์ดํ : {'์ด๋ก': 4, 'ํ๋': 2, '๋นจ๊ฐ': 4, '๋
ธ๋': 5}
__call__ ๋ฉ์๋๋ฅผ ๊ตฌํํด, ํด๋์ค๋ก ๊ด๋ฆฌ ํ ์ ์๋ค.
class BetterCountMissing:
def __init__(self):
self.added = 0
def __call__(self): # ๊ฐ์ฒด๋ฅผ ํจ์์ฒ๋ผ ํธ์ถ์ด ๊ฐ๋ฅ
self.added += 1
return 0
from collections import defaultdict
counter = BetterCountMissing()
colors = {'์ด๋ก': 1, 'ํ๋': 2}
increments = [('์ด๋ก',3), ('๋นจ๊ฐ',4), ('๋
ธ๋',5)]
result = defaultdict(counter, colors) # __call__ ๋ฅผ ํธ์ถํจ
print(f'์ด์ : {dict(result)}')
for color, count in increments:
result[color] += count
print(f"ํค๊ฐ ์ถ๊ฐ๋ ํ์ : {counter.added}")
print(f'์ดํ : {dict(result)}')
>>>
์ด์ : {'์ด๋ก': 1, 'ํ๋': 2}
ํค๊ฐ ์ถ๊ฐ๋ ํ์ : 2
์ดํ : {'์ด๋ก': 4, 'ํ๋': 2, '๋นจ๊ฐ': 4, '๋
ธ๋': 5}
Better way 40 super๋ก ๋ถ๋ชจ ํด๋์ค๋ฅผ ์ด๊ธฐํํ๋ผ
# ์ ์ข์ ์์ - ๋ถ๋ชจ ํด๋์ค๋ฅผ ์ง์ ํธ์ถํ์ฌ ์ด๊ธฐํ
class Base:
def __init__(self, value):
self.value = value
class Child(Base):
def __init__(self):
Base.__init__(self, 5) # ์ ์๋ํ๋ค.
๋ฌธ์ ์
- ๋ค์ค ์์์ ํ ๋, ํธ์ถ์ ์์๊ฐ ์ธ์์ ์์์ ๋ฌด๊ด
- ๋ค์ด์๋ชฌ๋ ์์์ ํ ๋, ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์๋ ์๋ค.
class Base:
def __init__(self, value):
self.value = value
class TimesSeven(Base):
def __init__(self, value):
Base.__init__(self, value)
self.value *= 7
class PlusNine(Base):
def __init__(self, value):
Base.__init__(self, value)
self.value += 9
class Diamond(TimesSeven, PlusNine):
def __init__(self, value):
TimesSeven.__init__(self, value)
PlusNine.__init__(self, value)
diamond = Diamond(5)
print(f'(5 * 7) + 9 = 44 ๋ฅผ ๊ธฐ๋ํ์ง๋ง, ์ค์ ๊ฐ {diamond.value}')
>>>
(5 * 7) + 9 = 44 ๋ฅผ ๊ธฐ๋ํ์ง๋ง, ์ค์ ๊ฐ 14
์๋ํ๋ฉด ์ TimesSenve.__init__ ๋๋ ํ, value ๋ 35 ์ด์ง๋ง, PlusNine์์ Base.__init__ ์ ๋ค์ ํ๋ฒ ๋ ํธ์ถํ์ฌ value๊ฐ 5๋ก ๋ค์ ์ด๊ธฐํ ๋ํ 5+9๋ฅผ ์คํํ์ฌ 14๊ฐ ๋๊ธฐ ๋๋ฌธ์ด๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด super()์ ์ฌ์ฉํ์. super()๋ ๋ค์ด์๋ชฌ๋ ๊ณ์ธต์์ ๊ณตํต ์์ ํด๋์ค๋ฅผ ๋จ ํ๋ฒ๋ง ํธ์ถํ๋๋ก ๋ณด์ฅํ๋ค.
- ํ์ค ๋ฉ์๋ ๊ฒฐ์ ์์ (Method Resolution Order, MRO) - C3 ์ ํํ ์๊ณ ๋ฆฌ์ฆ ์ฌ์ฉ
class Base:
def __init__(self, value):
self.value = value
class TimesSeven(Base):
def __init__(self, value):
super().__init__(value)
self.value *= 7
class PlusNine(Base):
def __init__(self, value):
super().__init__(value)
self.value += 9
class Diamond(PlusNine, TimesSeven):
def __init__(self, value):
super().__init__(value)
diamond = Diamond(5)
print(f'(5 * 7) + 9 = 44 ๋ฅผ ๊ธฐ๋ํ์ง๋ง, ์ค์ ๊ฐ {diamond.value}')
>>>
(5 * 7) + 9 = 44 ๋ฅผ ๊ธฐ๋ํ์ง๋ง, ์ค์ ๊ฐ 44
Better way 41 ๊ธฐ๋ฅ์ ํฉ์ฑํ ๋๋ ๋ฏน์ค์ธ ํด๋์ค๋ฅผ ์ฌ์ฉํ๋ผ
๋ฏน์ค์ธ(mix-in)
- ์์ ํด๋์ค๊ฐ ์ฌ์ฉํ ๋ฉ์๋ ๋ช ๊ฐ๋ง ์ ์ํ๋ ํด๋์ค
- ์์ฒด ์ ํธ๋ฆฌ๋ทฐํธ ์ ์๊ฐ ์์ผ๋ฏ๋ก ์์์ด ๋ถ๋ชจ ๋ฏน์ค์ธ ํด๋์ค __init__ ์ ํธ์ถํ ํ์๋ ์๋ค.
class LogMixin:
def log(self, message):
print(f"[LOG]: {message}")
class TimestampMixin:
def timestamp(self):
from datetime import datetime
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
class App(LogMixin, TimestampMixin):
def run(self):
self.log("์ฑ์ด ์คํ๋์์ต๋๋ค.")
print(f"ํ์ฌ ์๊ฐ: {self.timestamp()}")
app = App()
app.run()
Better way 42 ๋น๊ณต๊ฐ ์ ํธ๋ฆฌ๋ทฐํธ๋ณด๋ค๋ ๊ณต๊ฐ ์ ํธ๋ฆฌ๋ทฐํธ๋ฅผ ์ฌ์ฉํ๋ผ
ํ์ด์ฌ ํด๋์ค์ ์ดํธ๋ฆฌ๋ทฐํธ ๊ฐ์์ฑ
- ๊ณต๊ฐ (public)
- ๋น๊ณต๊ฐ (private)
class MyClass:
def __init__(self):
self.public_field = 5 # public
self.__private_field = 10 # private
def get_private_field(self):
return self.__private_field
my_class = MyClass()
print(my_class.public_field) # 5
print(my_class.get_private_field()) # 10
print(my_class.__private_field) # AttributeError: 'MyClass' object has no attribute
๋น๊ณต๊ฐ ์ดํธ๋ฆฌ๋ทฐํธ์ธ __private_field๋ฅผ ์ธ๋ถ์์ ์ ๊ทผํ๋ฉด ์์ธ๊ฐ ๋ฐ์ํ๋ค.
ํ์ด์ฌ์์ ๋น๊ณต๊ฐ ์ดํธ๋ฆฌ๋ทฐํธ์ ๋์์ ๊ทธ์ ์ด๋ฆ์ ๋ฐ๊พธ๋ ๋จ์ํ ๋ฐฉ์์ผ๋ก ๊ตฌํ๋๋ค.
์๋ฅผ ๋ค๋ฉด MyClass ๋ด์ __private_field ์ดํธ๋ฆฌ๋ทฐํธ๋ _MyClass__private_field ์ผ๋ก ๋ณ๊ฒฝ๋์ด ๋์ํ๋ค.
๊ทธ๋์ ํด๋น ์ด๋ฆ์ผ๋ก ์ธ๋ถ์์ ์ ๊ทผํ๋ฉด, ์ ๊ทผ์ด ๊ฐ๋ฅํ๋ค.
print(my_class._MyClass__private_field) # 10
์ ํ์ด์ฌ์์๋ ๊ฐ์์ฑ์ ์๊ฒฉํ๊ฒ ์ ํํ์ง ์์์๊น?
"์ฐ๋ฆฌ๋ ๋ชจ๋ ์ฑ ์์ง์ค ์๋ ์ฑ์ธ์ด๋ค" ๋ผ๋ ๋ชจํ ๋ก ์ ๋ง ๊ตณ์ด๊ตณ์ด ์ํ์ ํํ ํ์์ฑ์ด ์๋ค๋ฉด, ๊ฐ๋ฐ์์๊ฒ ์ ํ์ ์กด์คํ๋ ๊ฒ์ด๋ค.
๊ด๋ก์ ์ผ๋ก๋ _ 1๊ฐ๋ง ์๋ ๊ฒฝ์ฐ protect ์๋ฏธ๋ก ์กฐ์ฌํ ์ฌ์ฉํด์ผ ํ๋ค๋ ๋ป์ด๋ค.
Better way 43 ์ปค์คํ ์ปจํ ์ด๋ ํ์ ์ collections.abc๋ฅผ ์์ํ๋ผ
์ฅ์ 1. ํ์ํ ๋ฉ์๋๊ฐ ๊ตฌํ๋์ง ์์ ๊ฒฝ์ฐ, ์ค์ํ ๋ถ๋ถ์ ์๋ ค์ค๋ค.
from collections.abc import Sequence
class BadType(Sequence):
pass
foo = BadType()
>>>
foo = BadType()
^^^^^^^^^
TypeError: Can't instantiate abstract class BadType with abstract methods __getitem__, __len__
์ฅ์ 2. ์๊ตฌํ๋ ๋ฉ์๋๋ฅผ ๊ตฌํํ๋ฉด ๋๋จธ์ง ๋ฉ์๋ - Sequence์ ๊ฒฝ์ฐ (index, count)
# ์ง์ ๊ตฌํํ๋ ๊ฒฝ์ฐ
class IndexableNode:
def __init__(self, value, next_node=None):
self.value = value
self.next_node = next_node
def __getitem__(self, index):
current = self
for _ in range(index):
if current.next_node is None:
raise IndexError("Index out of range")
current = current.next_node
return current.value
def __len__(self):
count = 0
current = self
while current:
count += 1
current = current.next_node
return count
def __contains__(self, item):
current = self
while current:
if current.value == item:
return True
current = current.next_node
return False
def index(self, item):
current = self
idx = 0
while current:
if current.value == item:
return idx
current = current.next_node
idx += 1
raise ValueError(f"{item} is not in list")
def count(self, item):
current = self
count = 0
while current:
if current.value == item:
count += 1
current = current.next_node
return count
from collections.abc import Sequence
# โ
collections.abc Sequence๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ (๋ ๊ฐ๊ฒฐํ๊ณ ์ ์ง๋ณด์ ์ฉ์ด)
class IndexableNode(Sequence):
def __init__(self, value, next_node=None):
self.value = value
self.next_node = next_node
def __getitem__(self, index):
current = self
for _ in range(index):
if current.next_node is None:
raise IndexError("Index out of range")
current = current.next_node
return current.value
def __len__(self):
count = 0
current = self
while current:
count += 1
current = current.next_node
return count
# ์ฌ์ฉ ์์๋ ๋์ผํ๋ค
node3 = IndexableNode(3)
node2 = IndexableNode(2, node3)
node1 = IndexableNode(1, node2)
print(node1[1]) # 2
print(len(node1)) # 3
print(3 in node1) # True (์๋ ์ง์)
print(node1.index(2)) # 1 (์๋ ์ง์)
print(node1.count(3)) # 1 (์๋ ์ง์)
'๐ Python' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
์ดํํฐ๋ธ ํ์ด์ฌ 7์ฅ - ๋์์ฑ๊ณผ ๋ณ๋ ฌ์ฑ (0) | 2025.04.01 |
---|---|
์ดํํฐ๋ธ ํ์ด์ฌ 6์ฅ - ๋ฉํํด๋์ค์ ์ ํธ๋ฆฌ๋ทฐํธ (0) | 2025.03.30 |
์ดํํฐ๋ธ ํ์ด์ฌ 4์ฅ - ์ปดํ๋ฆฌํจ์ ๊ณผ ์ ๋๋ ์ดํฐ (0) | 2025.03.18 |
์ดํํฐ๋ธ ํ์ด์ฌ 3์ฅ - ํจ์ (0) | 2025.03.17 |
์ดํํฐ๋ธ ํ์ด์ฌ 2์ฅ - ๋ฆฌ์คํธ์ ๋์ ๋๋ฆฌ (0) | 2025.03.12 |