KR_MagicMethod - somaz94/python-study GitHub Wiki
κ°μ²΄μ κΈ°λ³Έ λμμ μ μνλ νΉλ³ν λ©μλλ€μ΄λ€.
class Number:
def __init__(self, value): # μ΄κΈ°ν
self.value = value
def __str__(self): # λ¬Έμμ΄ νν
return f"Number({self.value})"
def __repr__(self): # κ°λ°μλ₯Ό μν λ¬Έμμ΄ νν
return f"Number({self.value})"
def __bool__(self): # bool() ν¨μλ 쑰건문μμ νκ°
return self.value != 0
def __hash__(self): # ν΄μκ° λ°ν (λμ
λ리 ν€λ‘ μ¬μ© κ°λ₯)
return hash(self.value)
def __format__(self, format_spec): # format() ν¨μ λ° f-λ¬Έμμ΄μμ μ¬μ©
if format_spec == 'hex':
return f"0x{self.value:x}"
return str(self)
num = Number(42)
print(str(num)) # Number(42)
print(repr(num)) # Number(42)
print(bool(num)) # True
print(bool(Number(0))) # False
print(f"{num:hex}") # 0x2a
# ν΄μν
μ΄λΈ(λμ
λ리) ν€λ‘ μ¬μ©
d = {num: "answer"}
print(d[num]) # answer
β
νΉμ§:
- κ°μ²΄ μ΄κΈ°ν
- λ¬Έμμ΄ λ³ν
- λλ²κΉ νν
- μ§λ¦¬κ° νκ°
- ν΄μ κ°λ₯ κ°μ²΄
- νμν μ μ΄
Pythonμ μ°μ°μ λμμ μ¬μ©μ μ μ ν΄λμ€μ λ§κ² μ¬μ μν μ μλ€.
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other): # + μ°μ°μ
return Vector(self.x + other.x, self.y + other.y)
def __sub__(self, other): # - μ°μ°μ
return Vector(self.x - other.x, self.y - other.y)
def __mul__(self, scalar): # * μ°μ°μ (Vector * scalar)
return Vector(self.x * scalar, self.y * scalar)
def __rmul__(self, scalar): # * μ°μ°μ (scalar * Vector)
return self.__mul__(scalar)
def __truediv__(self, scalar): # / μ°μ°μ
return Vector(self.x / scalar, self.y / scalar)
def __eq__(self, other): # == μ°μ°μ
return self.x == other.x and self.y == other.y
def __lt__(self, other): # < μ°μ°μ
return (self.x**2 + self.y**2) < (other.x**2 + other.y**2)
def __abs__(self): # abs() ν¨μ
return (self.x**2 + self.y**2) ** 0.5
def __neg__(self): # -λ²‘ν° (λΆνΈ λ°μ )
return Vector(-self.x, -self.y)
def __str__(self):
return f"Vector({self.x}, {self.y})"
# μ°μ°μ μ¬μ© μμ
v1 = Vector(1, 2)
v2 = Vector(3, 4)
v3 = v1 + v2 # Vector(4, 6)
v4 = v1 * 2 # Vector(2, 4)
v5 = 3 * v1 # Vector(3, 6) - __rmul__ λλΆμ κ°λ₯
v6 = v2 / 2 # Vector(1.5, 2.0)
print(abs(v1)) # 2.23606... (벑ν°μ ν¬κΈ°)
print(v1 < v2) # True (v1μ ν¬κΈ° < v2μ ν¬κΈ°)
print(-v1) # Vector(-1, -2)
β
νΉμ§:
- μ°μ μ°μ°μ (add, sub, mul, truediv, λ±)
- μλ°©ν₯ μ°μ°μ (radd, rsub, rmul, λ±)
- λ³΅ν© ν λΉ μ°μ°μ (iadd, isub, λ±)
- λΉκ΅ μ°μ°μ (eq, ne, lt, gt, λ±)
- λ¨ν μ°μ°μ (pos, neg, abs, λ±)
- μ§κ΄μ μΈ κ°μ²΄ μ°μ° ꡬν
- μ°μ°μ μ°μ μμ μ μ§
리μ€νΈλ λμ
λ리 κ°μ 컨ν
μ΄λμ²λΌ λμνλ μ¬μ©μ μ μ ν΄λμ€λ₯Ό λ§λ€ μ μλ€.
class MyList:
def __init__(self, items):
self.items = items
def __len__(self): # len() ν¨μ
return len(self.items)
def __getitem__(self, index): # μΈλ±μ± λ° μ¬λΌμ΄μ±
return self.items[index]
def __setitem__(self, index, value): # νλͺ© μ€μ
self.items[index] = value
def __delitem__(self, index): # del μ°μ°μ
del self.items[index]
def __contains__(self, item): # in μ°μ°μ
return item in self.items
def __iter__(self): # forλ¬Έμμ λ°λ³΅
return iter(self.items)
def __reversed__(self): # reversed() ν¨μ
return reversed(self.items)
# 컨ν
μ΄λ λμ μ¬μ© μμ
lst = MyList([1, 2, 3, 4, 5])
print(len(lst)) # 5
print(lst[0]) # 1
print(lst[1:3]) # [2, 3] - μ¬λΌμ΄μ± μ§μ
lst[0] = 10 # νλͺ© μ€μ
print(lst[0]) # 10
print(3 in lst) # True
del lst[0] # 첫 λ²μ§Έ νλͺ© μμ
print(lst[0]) # 2
# λ°λ³΅ κ°λ₯
for item in lst:
print(item, end=' ') # 2 3 4 5
# μμ λ°λ³΅
print()
for item in reversed(lst):
print(item, end=' ') # 5 4 3 2
# λμ
λ리 μ€νμΌ μ»¨ν
μ΄λ
class SimpleDict:
def __init__(self):
self._data = {}
def __getitem__(self, key):
return self._data[key]
def __setitem__(self, key, value):
self._data[key] = value
def __delitem__(self, key):
del self._data[key]
def __contains__(self, key):
return key in self._data
def __iter__(self):
return iter(self._data)
d = SimpleDict()
d['name'] = 'νκΈΈλ'
print(d['name']) # νκΈΈλ
β
νΉμ§:
- μνμ€/λ§€ν λμ ꡬν
- μΈλ±μ±/μ¬λΌμ΄μ± μ§μ
- νλͺ© μμ λ° μμ
- λ©€λ²μ ν μ€νΈ (in μ°μ°μ)
- λ°λ³΅ κ°λ₯(iterable) κ°μ²΄
- 리μ€νΈ/νν/λμ λ리 μ μ¬ λμ
- μνμ€ νλ‘ν μ½ μ€μ
κ°μ²΄μ μμ±, μ¬μ©, μλ©Έ μ£ΌκΈ°λ₯Ό κ΄λ¦¬νλ λ§€μ§ λ©μλλ€μ΄λ€.
class Resource:
def __init__(self, name):
self.name = name
print(f"{self.name} 리μμ€ μ΄κΈ°ν")
def __del__(self): # μλ©Έμ
print(f"{self.name} 리μμ€ μ 리")
def __enter__(self): # with λ¬Έ μμ
print(f"{self.name} 컨ν
μ€νΈ μμ")
return self
def __exit__(self, exc_type, exc_val, exc_tb): # with λ¬Έ μ’
λ£
print(f"{self.name} 컨ν
μ€νΈ μ’
λ£")
if exc_type is not None:
print(f"μμΈ λ°μ: {exc_val}")
return False # Falseλ©΄ μμΈ μ ν, Trueλ©΄ μμΈ μ΅μ
# μΌλ° μ¬μ©
r = Resource("μΌλ°")
del r # λͺ
μμ μλ©Έ
# with λ¬Έ μ¬μ© (μ μ)
with Resource("μ μ") as r:
print("리μμ€ μ¬μ© μ€")
# with λ¬Έ μ¬μ© (μμΈ λ°μ)
try:
with Resource("μμΈ") as r:
print("리μμ€ μ¬μ© μ€")
raise ValueError("μμΈ ν
μ€νΈ")
except ValueError:
print("μμΈ μ²λ¦¬λ¨")
# μ μ€νμΌ μ»¨ν
μ€νΈ κ΄λ¦¬μ (Python 3.10+)
from contextlib import contextmanager
class ModernResource:
def __init__(self, name):
self.name = name
@contextmanager
def session(self):
print(f"{self.name} μΈμ
μμ")
try:
yield self
print(f"{self.name} μΈμ
μ μ μ’
λ£")
except Exception as e:
print(f"{self.name} μΈμ
μμΈ μ’
λ£: {e}")
raise
mr = ModernResource("νλμ")
with mr.session() as session:
print("μΈμ
μ¬μ© μ€")
β
νΉμ§:
- κ°μ²΄ μ΄κΈ°ν (init)
- κ°μ²΄ μλ©Έ μ²λ¦¬ (del)
- 컨ν μ€νΈ κ΄λ¦¬μ νλ‘ν μ½ (enter, exit)
- 리μμ€ κ΄λ¦¬ μλν
- μμΈ μ²λ¦¬ λ° μ 리
- with λ¬Έ νμ©
- μμ ν 리μμ€ ν΄μ 보μ₯
κ°μ²΄μ μμ± μ κ·Όμ μ μ΄νκ³ μ»€μ€ν°λ§μ΄μ§ν μ μλ€.
class Person:
def __init__(self, name, age):
self._name = name
self._age = age
self._protected = {}
def __getattr__(self, name): # μ‘΄μ¬νμ§ μλ μμ± μ κ·Ό
return f"{name}μ μ‘΄μ¬νμ§ μλ μμ±μ
λλ€."
def __getattribute__(self, name): # λͺ¨λ μμ± μ κ·Ό
if name.startswith('_secret_'):
raise AttributeError(f"{name}μ μ κ·Όν μ μμ΅λλ€.")
# 무ν μ¬κ· λ°©μ§λ₯Ό μν΄ super() μ¬μ©
return super().__getattribute__(name)
def __setattr__(self, name, value): # μμ± μ€μ
if name == 'age' and value < 0:
raise ValueError("λμ΄λ μμκ° λ μ μμ΅λλ€.")
print(f"{name} μμ±μ {value}λ‘ μ€μ ")
super().__setattr__(name, value)
def __delattr__(self, name): # μμ± μμ
if name == '_name':
raise AttributeError("μ΄λ¦μ μμ ν μ μμ΅λλ€.")
print(f"{name} μμ± μμ ")
super().__delattr__(name)
# νλ‘νΌν° μ€νμΌ μ κ·Ό
@property
def name(self):
return self._name
@property
def age(self):
return self._age
@age.setter
def age(self, value):
if value < 0:
raise ValueError("λμ΄λ μμκ° λ μ μμ΅λλ€.")
self._age = value
# λμ μμ± μ§μ
def __getitem__(self, key):
return self._protected.get(key, None)
def __setitem__(self, key, value):
self._protected[key] = value
# μμ± μ κ·Ό μμ
p = Person("νκΈΈλ", 30)
print(p.name) # νκΈΈλ
p.age = 35 # age μμ±μ 35λ‘ μ€μ
print(p.age) # 35
try:
p.age = -5 # ValueError: λμ΄λ μμκ° λ μ μμ΅λλ€.
except ValueError as e:
print(e)
print(p.address) # addressμ μ‘΄μ¬νμ§ μλ μμ±μ
λλ€.
try:
print(p._secret_data) # AttributeError
except AttributeError as e:
print(e)
# λμ
λ리 μ€νμΌ μ‘μΈμ€
p['location'] = 'μμΈ'
print(p['location']) # μμΈ
β
νΉμ§:
- μμ± μ κ·Ό μ μ΄
- μμ± μ‘΄μ¬ μ¬λΆ νμΈ
- λμ μμ± μ²λ¦¬
- μ κ·Ό μ ν λ° μ ν¨μ± κ²μ¦
- νλ‘νΌν° μ€νμΌ μ‘μΈμ€
- λμ λ리 μ€νμΌ μ‘μΈμ€
- λ΄λΆ μμ± λ³΄νΈ
ν¨μμ²λΌ νΈμΆν μ μλ κ°μ²΄μ μμ± μ κ·Όμ μ¬μ©μννλ μ€λͺ
μ ν¨ν΄μ΄λ€.
# νΈμΆ κ°λ₯ κ°μ²΄ (Callable)
class Adder:
def __init__(self, n):
self.n = n
def __call__(self, x): # κ°μ²΄λ₯Ό ν¨μμ²λΌ νΈμΆ
return self.n + x
add5 = Adder(5)
print(add5(10)) # 15 - κ°μ²΄λ₯Ό ν¨μμ²λΌ νΈμΆ
print(callable(add5)) # True
# μ€λͺ
μ (Descriptor)
class Validator:
def __init__(self, min_value=None, max_value=None):
self.min_value = min_value
self.max_value = max_value
self.name = None
def __set_name__(self, owner, name): # Python 3.6+
self.name = name
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__[self.name]
def __set__(self, instance, value):
if self.min_value is not None and value < self.min_value:
raise ValueError(f"{self.name}μ(λ) {self.min_value} μ΄μμ΄μ΄μΌ ν©λλ€.")
if self.max_value is not None and value > self.max_value:
raise ValueError(f"{self.name}μ(λ) {self.max_value} μ΄νμ¬μΌ ν©λλ€.")
instance.__dict__[self.name] = value
class Person:
age = Validator(min_value=0, max_value=150)
height = Validator(min_value=0)
def __init__(self, name, age, height):
self.name = name
self.age = age
self.height = height
# μ€λͺ
μ μ¬μ© μμ
p = Person("νκΈΈλ", 30, 175)
print(p.age) # 30
try:
p.age = -5 # ValueError
except ValueError as e:
print(e)
try:
p.age = 200 # ValueError
except ValueError as e:
print(e)
# ν¨μ μ₯μμμ λ§€μ§ λ©μλ κ²°ν©
def debug_calls(cls):
original_methods = {}
# λͺ¨λ λ§€μ§ λ©μλ μ°ΎκΈ°
for name, method in vars(cls).items():
if callable(method) and name.startswith('__') and name.endswith('__'):
original_methods[name] = method
# λ§€μ§ λ©μλ λν
def wrap_method(name, method):
def wrapper(self, *args, **kwargs):
print(f"νΈμΆ: {cls.__name__}.{name}({args}, {kwargs})")
result = method(self, *args, **kwargs)
print(f"κ²°κ³Ό: {result}")
return result
return wrapper
# λνλ λ©μλλ‘ κ΅μ²΄
for name, method in original_methods.items():
setattr(cls, name, wrap_method(name, method))
return cls
@debug_calls
class DebuggableVector(Vector):
pass
# λλ²κΉ
λνΌ ν
μ€νΈ
dv1 = DebuggableVector(1, 2)
dv2 = DebuggableVector(3, 4)
result = dv1 + dv2 # νΈμΆ λ° κ²°κ³Ό λ‘κΉ
μ΄ μΆλ ₯λ¨
β
νΉμ§:
- νΈμΆ κ°λ₯ κ°μ²΄ (call)
- ν¨μν κ°μ²΄ ꡬν
- μ€λͺ μ νλ‘ν μ½ (get, set, delete)
- μμ± μ κ·Ό μ¬μ©μν
- μμ± μ ν¨μ± κ²μ¬
- μ¬μ¬μ© κ°λ₯ν μμ± λ‘μ§
- λ°μ΄ν° μΊ‘μν
- λ©ννλ‘κ·Έλλ° κΈ°λ²
λ μμ£Ό μ¬μ©λμ§λ§ κ°λ ₯ν κΈ°λ₯μ μ 곡νλ λ§€μ§ λ©μλλ€μ΄λ€.
# νΌν΄λ§ μ§μ
import pickle
class PickleableClass:
def __init__(self, value):
self.value = value
self._cache = {} # νΌν΄λ§λμ§ μμ μΊμ
def __getstate__(self): # νΌν΄λ§ μ νΈμΆ
# _cacheλ μ μΈνκ³ νΌν΄λ§
state = self.__dict__.copy()
del state['_cache']
return state
def __setstate__(self, state): # μΈνΌν΄λ§ μ νΈμΆ
self.__dict__ = state
self._cache = {} # μΊμ μ΄κΈ°ν
obj = PickleableClass(42)
obj._cache['temp'] = 'temporary data'
# νΌν΄λ§ λ° μΈνΌν΄λ§
serialized = pickle.dumps(obj)
deserialized = pickle.loads(serialized)
print(deserialized.value) # 42
print(hasattr(deserialized, '_cache')) # True
print(deserialized._cache) # {} (λΉμ΄μμ)
# μ¬μ©μ μ μ μ«μ νμ
class Fraction:
def __init__(self, numerator, denominator):
self.numerator = numerator
self.denominator = denominator
self._simplify()
def _simplify(self):
# μ΅λ곡μ½μ κ³μ°
def gcd(a, b):
while b:
a, b = b, a % b
return a
g = gcd(abs(self.numerator), abs(self.denominator))
self.numerator //= g
self.denominator //= g
def __int__(self): # int() λ³ν
return self.numerator // self.denominator
def __float__(self): # float() λ³ν
return self.numerator / self.denominator
def __round__(self, ndigits=0): # round() ν¨μ
return round(float(self), ndigits)
def __complex__(self): # complex() λ³ν
return complex(float(self), 0)
def __str__(self):
return f"{self.numerator}/{self.denominator}"
frac = Fraction(22, 7) # νμ΄μ κ·Όμ¬κ°
print(str(frac)) # 22/7
print(int(frac)) # 3
print(float(frac)) # 3.142857...
print(round(frac, 2)) # 3.14
print(complex(frac)) # (3.142857...+0j)
# __slots__ μ¬μ©μΌλ‘ λ©λͺ¨λ¦¬ μ΅μ ν
class Point:
__slots__ = ('x', 'y') # νμ©λ μμ± μ ν
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(3, 4)
p.x = 5 # μ μ μλ
try:
p.z = 10 # AttributeError: 'Point' object has no attribute 'z'
except AttributeError as e:
print(e)
β
νΉμ§:
- κ°μ²΄ μ§λ ¬ν (getstate, setstate)
- νμ λ³ν λ©μλ (int, float, complex, λ±)
- λ©λͺ¨λ¦¬ μ΅μ ν (slots)
- μμΉ μ°μ° μ§μ
- κ°μ²΄ λΆλ₯ λ° λ±λ‘
- νμ ν΄λμ€ νμΈ
- λ©νν΄λμ€ νλ‘ν μ½
- μ¬μ©μ μ μ 컨ν μ΄λ λμ
β
λͺ¨λ² μ¬λ‘:
-
__init__
κ³Ό__str__
κΈ°λ³Έ ꡬννκΈ° -
__repr__
μ κ°λ₯νλ©΄ κ°μ²΄ μ¬μμ± μ½λ λ°ν - μ°μ°μ μ€λ²λ‘λ©μ μ§κ΄μ μ΄κ³ μΌκ΄λκ² κ΅¬ν
- 컨ν
μ΄λ νμ
μ
__len__
κ³Ό__getitem__
ꡬν - 리μμ€ κ΄λ¦¬λ
__enter__
μ__exit__
μ¬μ© - μμ± μ κ·Ό μ μ΄λ μ μ€νκ² μ€κ³
-
__eq__
λ₯Ό ꡬνν λ__hash__
λ ν¨κ» ꡬν - λ©λͺ¨λ¦¬ λ° μ±λ₯ μ΅μ νλ₯Ό μν΄
__slots__
μ¬μ© κ³ λ € - μΌκ΄μ± μλ λμ μ μ§νκΈ°
- νμ€ λΌμ΄λΈλ¬λ¦¬ κ·μ½ λ°λ₯΄κΈ°
- λ§€μ§ λ©μλ νΈμΆ μ λ΄μ₯ ν¨μ μ¬μ©νκΈ° (μ§μ νΈμΆ νΌνκΈ°)
- μ€λͺ μμ νλ‘νΌν° νμ©νμ¬ μμ± λ‘μ§ μ¬μ¬μ©
- μ€λ₯ μ²λ¦¬ λ° μμΈ λ°μ λͺ νν νκΈ°
- λ§€μ§ λ©μλ μ¬μ©λ²μ νμ€ λΌμ΄λΈλ¬λ¦¬ μ½λ μ°Έμ‘°
- κ³Όλν 컀μ€ν°λ§μ΄μ§ νΌνκ³ μ§κ΄μ μΈ μΈν°νμ΄μ€ μ μ§