Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save neoneo40/7199cde18a9399c2fa62 to your computer and use it in GitHub Desktop.
Save neoneo40/7199cde18a9399c2fa62 to your computer and use it in GitHub Desktop.
How to classify between return and instance variable?
import requests
from bs4 import BeautifulSoup
# No.1
class CrawlerWithReturn(object):
def __init__(self, url):
self.url = url
def get_page(self):
return requests.get(self.url)
def get_soup(self, resp):
return BeautifulSoup(resp.text, 'lxml')
def get_infos_in_page(self, soup):
return soup.title
def main():
url = 'http://naver.com'
c = CrawlerWithReturn(url)
resp = c.get_page()
soup = c.get_soup(resp)
title = c.get_infos_in_page(soup)
print(title.text)
print(main())
# NAVER
# No.2
import requests
from bs4 import BeautifulSoup
class CrawlerWithInstanceVariable(object):
def __init__(self, url):
self.url = url
def get_page(self):
self.resp = requests.get(self.url)
def get_soup(self):
self.soup = BeautifulSoup(self.resp.text, 'lxml')
def get_infos_in_page(self):
self.title = self.soup.title
def main():
url = 'http://naver.com'
c = CrawlerWithInstanceVariable(url)
c.get_page()
c.get_soup()
c.get_infos_in_page()
print(c.title.text)
main()
# NAVER
# No.3
# 이건 2개를 섞어서 써서 더 헷깔리는것 같음..
import requests
from bs4 import BeautifulSoup
class CrawlerWithReturnAndInstanceVariable(object):
def __init__(self, url):
self.url = url
def get_page(self):
resp = requests.get(self.url)
self.resp = resp
return resp
def get_soup(self):
soup = BeautifulSoup(self.resp.text, 'lxml')
self.soup = soup
return soup
def get_infos_in_page(self):
title = self.soup.title
self.title = title
return title
def main():
url = 'http://naver.com'
c = CrawlerWithInstanceVariable(url)
resp = c.get_page()
c.get_soup()
title = c.get_infos_in_page()
print(title.text)
main()
# NAVER

How to classify between return and instance variable?

  • CrawlerWithReturn 으로 된 것은 함수의 기본에 맞게끔 모두 return 문을 사용해서 받아 온 후에 main() 에서 실행합니다.
  • 함수의 정석이라고 할 수 있겠죠. 근데 매번 파라미터를 넘기고 변수로 return 값을 받기가 귀찮네요.
  • 그래서 좀 더 편하게 해보려고 인스턴스 변수를 사용해 봤습니다.
  • 그런데 왠지 정석이 아닌것 같아서 좀 쓰기가 그렇습니다만 파라미터로 넘기는 것도 없고 리턴으로 받는 것도 없게 되서
  • 프로그램 짜기가 좀 더 편해졌습니다. 다만, 인스턴스 변수를 쓰기 떄문에 함수의 흐름을 제대로 보고 실행시켜 줘야겠지요..
  • 각각 언제 활용해야 하며 장,단점이 무엇일까요?
  • 이 생각을 오랫동안 했었는데 물어보기 좀 그래서 놔뒀었는데.. 정말 궁금해졌습니다.
  • 혹시 이것보다 더 좋은 방법이 있을까요?

Code Complete 책 참고

  • Code Complete 이 책의 p.352 페이지를 보면 클래스의 멤버 데이터를 생성자에서 초기화 한다 이렇게 써있네요.
    • 루틴의 변수를 각 루틴 내에서 초기화해야 하는것처럼, 클래스의 데이터도 생성자에서 초기화 되어야 한다. 만약 메모리가 생성자에서 할당된다면, 소멸자에서 해제되어야 한다.
    • 라고 써져있는걸 보니.. 제가 변칙적으로 쓰는건 좀 아닌것 같네요?

p.492 페이지에 순서가 중요한 명령문, 순서가 중요하지 않은 명령문

명령문의 순서가 중요한 자바 예제

data = ReadData()
results = CalculateResultsFromData(data)
PrintResults(results)
  • 이 예제의 내적인 개념은 의존성에 관한 것
  • 3번째 명령문은 2번째 명령문에 의존하며, 2번째 명령문은 1번째 명령문에 의존한다. 이 예제에서 한 명령문이 다른 명령문에 의존한다는 사실은 루틴의 이름으로부터 명확하게 알 수 있다. 다음 코드에서는 의존성이 덜 분명하다.

명령문의 순서가 중요하지만 덜 분명한 자바 예제

revenue.ComputeMonthly();
revenue.ComputeQuarterly();
revenue.ComputeAnnual();
  • 이 예제에서 분기별 수입 계산은 월별 수입이 이미 계산되어 있다고 가정하고 있다. 회계에 대해서 알고 있다면, 또는 상식적으로 분기별 수입은 1년 수입이 계산되기 전에 계산되어져야 한다는 것을 알 수 있을 것이다. 이러한 계산들 간에는 의존성이 있다. 하지만 이 코드를 읽는 것만으로는 그러한 관계가 분명하지 않다. 이 코드에서는 그러한 의존성이 분명하지 않으며 실제로 감추어져 있다.
ComputeMarketinExpense
ComputeSalesExpense
ComputeTravelExpense
ComputePersonnelExpense
DisplayExpenseSummary
  • ComputeMarketingExpense()가 다른 모든 루틴에서 자신의 데이터를 넣을 클래스 멤버 변수들을 초기화한다고 가정해 보자. 그런 경우라면, 다른 루틴들보다 먼저 호출되어야 한다. 하지만 이 코드를 보고 그러한 내용을 어떻게 알 수 있을까? 이 루틴 호출은 아무런 매개변수를 갖고 있기 않기 때문에, 이 루틴들은 클래스 데이터에 접근하고 있다고 추측할 수 있을 것이다. 하지만 이 코드를 봐서는 확실하게 알 수 없다.

명령문들이 특정한 순서대로 작성되어야 하는 의존성을 갖고 있을 때, 의존성을 분명히 하기 위한 단계를 밟아야 한다.

  1. 의존성이 분명하도록 코드를 구성한다.
  2. 의존성이 분명하도록 루틴의 이름을 작성한다.
  3. 의존성을 분명히 하기 위해서 루틴 매개변수를 사용하라

데이터가 순서의 의존성을 말해주는 비주얼 베이직 예제

InitializeExpenseData(expenseData)
ComputeMarketingExpense(expenseData)
ComputeSalesExpense(expenseData)
ComputeTravelExpense(expenseData)
ComputePersonnelExpense(expenseData)
DisplayExpenseSummary(expenseData)
  • 모든 루틴들이 expenseData를 사용하기 때문에, 이 루틴들이 동일한 데이터를 다루고 있으며 명령문의 순서가 중요할지도 모른다는 힌트를 얻을 수 있다.
  • 이 예제에서는 루틴을 expenseData를 입력으로 받고 갱신된 expenseData를 출력으로 반환하는 함수로 변환하여, 코드에 순서 의존성이 있음을 보다 명확하게 만들 수 있다.

데이터와 루틴 호출이 순서의 의존성을 말해주는 비주얼 베이직 예제

expenseData = InitializeExpenseData(expenseData)
expenseData = ComputeMarketingExpense(expenseData)
expenseData = ComputeSalesExpense(expenseData)
expenseData = ComputeTravelExpense(expenseData)
expenseData = ComputePersonnelExpense(expenseData)
DisplayExpenseSummary(expenseData)
  • 또한 이 경우에서처럼 데이터의 실행 순서가 중요하지 않다는 것을 암시할 수도 있다.

데이터가 순서의 의존성을 암시하지 않는 비주얼 베이직 예제

ComputemarketingExpense(marketingData)
ComputeSalesExpense(salesData)
ComputeTravelExpense(travelData)
ComputePersonnelExpense(personnelData)
DisplayExpenseSummary(marketingData, salesData, travelData, personnelData)
  • 처음 네 줄에 있는 루틴들이 공통적으로 사용하는 데이터가 없기 떄문에, 이 코드는 이 루틴들이 호출되는 순서가 중요하지 않음을 암시하고 있다. 다섯 번째 줄에 있는 루틴은 처음 4개의 루틴으로부터 얻은 데이터를 사용하기 때문에, 이 함수는 아프이 4개의 루틴 다음에 실행되어야 한다는 것을 추측할 수 있다.

의존성의 분명하지 않은 부분은 주석으로 문서화한다.

명령문의 순서 의존성이 감추어져 있지만 주석으로 이를 분명히 한 비주얼 베이직 예제

` 지출 데이터를 계산한다. 각 루틴들은 멤버 데이터 expenseData에 접근한다.
` DisplayExpenseSummary는 다른 루틴들에서 계산된 데이터에 의존하기 떄문에
` 마지막에 호출되어야 한다.
  • 이 코드는 순서 의존성을 만들기 위한 기법을 사용하지 않고 있다. 물론, 주석보다는 그러한 기법들을 사용하는 것이 더 좋은 방법이겠지만, 만약 제약이 많은 코드를 유지 보수하고 있거나 어떠한 이유 때문에 코드 자체를 개선할 수 없다면, 코드의 약점을 보완하기 위해서 주석을 사용한다.

  • 어설션이나 오류 처리 코드로 의존성을 검사한다.

@neoneo40
Copy link
Author

neoneo40 commented Dec 6, 2015

페이스북 윤태호님 답변

  • 아하~~제일 밑에 있었군요~^^ 글쎄요 후자의 경우 뭔가 안좋다기보단 네이밍이 잘못된듯하다는 의견드리고 싶어요~ get으로 시작하는 함수는 뭔가를 리턴받는 경우가 많은데요. 조근영님께서 작성하신 코드는 내부의 변수에 상태를 바꾸는것이므로 set으로 시작하는 함수가되어야 맞다는 생각이드네요~^^ 그래야 남들이 봤을때도 한번에 알수있겠죠~ 여기서 뭔가를 셋팅하는구나 하구요~
  • 그리고 조근영님께서 작성하시는 방법은 많이쓰이는 방법인데요 보통은 재귀함수를 작성할때이거나 get이라는 함수를 호출했을때 값이 자주갱신되는 경우가 아닐경우에 인스턴스 변수에 저장해두었다가 변화가 없을경우 리턴하여 계산속도를 높이는 방법으로 많이쓰곤하기 때문에 용도에 맞게 쓰시면 되지않을까 합니다~^^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment