소개
이 섹션에서는 메서드 정의와 함께 사용되는 몇 가지 내장 데코레이터 (decorator) 에 대해 설명합니다.
사전 정의된 데코레이터 (Predefined Decorators)
클래스 정의에서 특별한 종류의 메서드를 지정하는 데 사용되는 사전 정의된 데코레이터가 있습니다.
class Foo:
def bar(self,a):
...
@staticmethod
def spam(a):
...
@classmethod
def grok(cls,a):
...
@property
def name(self):
...
하나씩 살펴보겠습니다.
정적 메서드 (Static Methods)
@staticmethod는 소위 정적 클래스 메서드 (C++/Java 에서) 를 정의하는 데 사용됩니다. 정적 메서드는 클래스의 일부이지만 인스턴스에 대해 작동하지 않는 함수입니다.
class Foo(object):
@staticmethod
def bar(x):
print('x =', x)
>>> Foo.bar(2) ## x=2
>>>
정적 메서드는 때때로 클래스에 대한 내부 지원 코드를 구현하는 데 사용됩니다. 예를 들어, 생성된 인스턴스 (메모리 관리, 시스템 리소스, 지속성, 잠금 등) 를 관리하는 데 도움이 되는 코드입니다. 특정 디자인 패턴 (여기서는 논의하지 않음) 에서도 사용됩니다.
클래스 메서드 (Class Methods)
@classmethod는 클래스 메서드를 정의하는 데 사용됩니다. 클래스 메서드는 인스턴스 대신 클래스 객체를 첫 번째 매개변수로 받습니다.
class Foo:
def bar(self):
print(self)
@classmethod
def spam(cls):
print(cls)
>>> f = Foo()
>>> f.bar()
<__main__.Foo object at 0x971690> ## The instance `f`
>>> Foo.spam()
<class '__main__.Foo'> ## The class `Foo`
>>>
클래스 메서드는 대체 생성자를 정의하는 도구로 가장 자주 사용됩니다.
class Date:
def __init__(self,year,month,day):
self.year = year
self.month = month
self.day = day
@classmethod
def today(cls):
## Notice how the class is passed as an argument
tm = time.localtime()
## And used to create a new instance
return cls(tm.tm_year, tm.tm_mon, tm.tm_mday)
d = Date.today()
클래스 메서드는 상속과 같은 기능으로 몇 가지 까다로운 문제를 해결합니다.
class Date:
...
@classmethod
def today(cls):
## Gets the correct class (e.g. `NewDate`)
tm = time.localtime()
return cls(tm.tm_year, tm.tm_mon, tm.tm_mday)
class NewDate(Date):
...
d = NewDate.today()
연습 문제 7.11: 실전에서의 클래스 메서드
report.py 및 portfolio.py 파일에서 Portfolio 객체의 생성은 약간 혼란스럽습니다. 예를 들어, report.py 프로그램에는 다음과 같은 코드가 있습니다.
def read_portfolio(filename, **opts):
'''
Read a stock portfolio file into a list of dictionaries with keys
name, shares, and price.
'''
with open(filename) as lines:
portdicts = fileparse.parse_csv(lines,
select=['name','shares','price'],
types=[str,int,float],
**opts)
portfolio = [ Stock(**d) for d in portdicts ]
return Portfolio(portfolio)
그리고 portfolio.py 파일은 다음과 같이 이상한 초기자를 사용하여 Portfolio()를 정의합니다.
class Portfolio:
def __init__(self, holdings):
self._holdings = holdings
...
솔직히 말해서, 코드 분산 때문에 책임의 연쇄가 다소 혼란스럽습니다. Portfolio 클래스가 Stock 인스턴스 목록을 포함해야 한다면, 클래스를 좀 더 명확하게 변경해야 할 수 있습니다. 다음과 같이:
## portfolio.py
import stock
class Portfolio:
def __init__(self):
self._holdings = []
def append(self, holding):
if not isinstance(holding, stock.Stock):
raise TypeError('Expected a Stock instance')
self._holdings.append(holding)
...
CSV 파일에서 포트폴리오를 읽으려면 클래스 메서드를 만들 수 있습니다.
## portfolio.py
import fileparse
import stock
class Portfolio:
def __init__(self):
self._holdings = []
def append(self, holding):
if not isinstance(holding, stock.Stock):
raise TypeError('Expected a Stock instance')
self._holdings.append(holding)
@classmethod
def from_csv(cls, lines, **opts):
self = cls()
portdicts = fileparse.parse_csv(lines,
select=['name','shares','price'],
types=[str,int,float],
**opts)
for d in portdicts:
self.append(stock.Stock(**d))
return self
이 새로운 Portfolio 클래스를 사용하려면 다음과 같은 코드를 작성할 수 있습니다.
>>> from portfolio import Portfolio
>>> with open('portfolio.csv') as lines:
... port = Portfolio.from_csv(lines)
...
>>>
Portfolio 클래스에 이러한 변경 사항을 적용하고 클래스 메서드를 사용하도록 report.py 코드를 수정하십시오.
요약
축하합니다! 데코레이터 메서드 (Decorated Methods) 랩을 완료했습니다. LabEx 에서 더 많은 랩을 연습하여 실력을 향상시킬 수 있습니다.