0

My generate_report function has 2 parameters. Within the generate_report function I have another function named generate_sub_report. I want to use the parameters report_type and date_range within generate_sub_report.

def generate_report(report_type, date_range):
  with open('file.csv') as f:
    # do some stuff
  def generate_sub_report():
    report_map = {'Budget': ['a', 'b', 'c'], 'Finance': ['d', 'e', 'f']}
    get_list_from_dict = report_map.get(report_type)
    if date_range == 'Summary':
       columns = ...

How do I use the function parameters report_type and date_range within generate_sub_report too? What I'm trying to say is, how can I make generate_sub_report inherit the parameters from generate_report?

Edited/Updated Question

Run the code below.

It throws: UnboundLocalError: local variable 'date_range' referenced before assignment

If I change def get_keyword_report_request(report_type, date_range): with the thought that maybe I have to pass the parameters through the nested function, it then throws:

TypeError: get_keyword_report_request() missing 2 required positional arguments: 'report_type' and 'date_range'

def generate_report(report_type, date_range):
  def get_keyword_report_request():
    columns_map = {
        "Account": ["AccountName", "AccountId", "Impressions", "Clicks", "Spend",
                    "Conversions"],
        "Campaign": ["AccountName", "AccountId", "CampaignName", "Status", "Impressions",
                     "Clicks", "Spend", "Conversions"],
        "Keyword": ["AccountName", "AccountId", "CampaignName", "CampaignStatus",
                    "AdGroupName", "AdGroupStatus", "Keyword", "KeywordStatus",
                    "BidMatchType", "CurrentMaxCpc", "Impressions", "Clicks", "Spend",
                    "AveragePosition", "Conversions"],
        "Ad group": ["AccountName", "AccountId", "CampaignName", "AdGroupName", "Status",
                     "Impressions", "Clicks", "Spend", "AveragePosition", "Conversions"],
        "Search Query": ["AccountName", "AccountId", "CampaignName", "CampaignStatus",
                         "AdGroupName", "SearchQuery", "Keyword", "BidMatchType",
                         "DeliveredMatchType", "Impressions", "Clicks", "Spend",
                         "AveragePosition", "Conversions"],
        "Ad": ["AccountName", "AccountId", "CampaignName", "AdGroupName", "Status",
               "AdTitle", "AdDescription", "DisplayUrl", "DestinationUrl", "AdStatus",
               "Impressions", "Clicks", "Spend", "AveragePosition", "Conversions"]
    }

    columns = columns_map.get(report_type)

    if isinstance(date_range, list):
      # do this
      print('wha')
    elif isinstance(date_range, str):
      date_range = date_range
      print(date_range)
    return(date_range, report_type)
  get_keyword_report_request()
generate_report('Keyword', 'Summary')

My initial question still remains: How can I use the top-level function's parameters in a nested/inner function within the larger function? This is probably super-basic and I'm probably an idiot, thumbs down, that's fine. I clearly don't comprehend something very fundamental. Sorry for the lack of clarity initially.

martineau
  • 119,623
  • 25
  • 170
  • 301
Jarad
  • 17,409
  • 19
  • 95
  • 154
  • 1
    This already *works*. The `report_type` and `date_range` names are available as closures in `generate_sub_report`. Are you having issues with this approach? – Martijn Pieters Sep 19 '15 at 06:42
  • I guess that this is related to this question: [Function inside function](http://stackoverflow.com/questions/4831680/function-inside-function) – perror Sep 19 '15 at 06:44

1 Answers1

3

The report_type is already available to the generate_subreport because it is automatically captured. Here's the proof:

>>> def generate_report(report_type):
...    def generate_subreport():
...       print "the report type is " + report_type
...    generate_subreport()
...
>>> generate_report('Big Report')
the report type is Big Report
>>> generate_report('Small Report')
the report type is Small Report
>>>

Update

Congratulations, you've hit one of the known Python warts. Any variables that are assigned are assumed to be a local variable.

This works:

>>> def generate_report(date_range):
...   def get_keyword_report_request():
...     print date_range
...   get_keyword_report_request()
...
>>> generate_report('Annually')
Annually

But this fails

>> def generate_report(date_range):
...   def get_keyword_report_request():
...     print date_range
...     date_range = 'Monthly' # one extra line
...   get_keyword_report_request()
...
>>> generate_report('Annually')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 5, in generate_report
  File "<stdin>", line 3, in get_keyword_report_request
UnboundLocalError: local variable 'date_range' referenced before assignment

Python saw that you're assigning to the date_range variable in the inner function, and decided that it needed to declare a new date_range local variable in the inner function, and when you call print date_range, it throws and error because the inner date_range has not been initialised yet.

Your fix - is to remove the date_range = date_range assignment. It does absolutely nothing.

def generate_report(report_type, date_range):
  def get_keyword_report_request(): 
    columns_map = {'Account': ['AccountName', 'AccountId', 'Impressions', 'Clicks', 'Spend', 'Conversions'],
             'Campaign': ['AccountName', 'AccountId', 'CampaignName', 'Status', 'Impressions', 'Clicks', 'Spend', 'Conversions'],
             'Ad group': ['AccountName', 'AccountId', 'CampaignName', 'AdGroupName', 'Status', 'Impressions', 'Clicks', 'Spend', 'AveragePosition', 'Conversions'], #Adgroup status?
             'Ad': ['AccountName', 'AccountId', 'CampaignName', 'AdGroupName', 'Status', 'AdTitle', 'AdDescription', 'DisplayUrl', 'DestinationUrl', 'AdStatus', 'Impressions', 'Clicks', 'Spend', 'AveragePosition', 'Conversions'],
             'Keyword': ['AccountName', 'AccountId', 'CampaignName', 'CampaignStatus', 'AdGroupName', 'AdGroupStatus', 'Keyword', 'KeywordStatus', 'BidMatchType', 'CurrentMaxCpc', 'Impressions', 'Clicks', 'Spend', 'AveragePosition', 'Conversions'],
             'Search Query': ['AccountName', 'AccountId', 'CampaignName', 'CampaignStatus', 'AdGroupName', 'SearchQuery', 'Keyword', 'BidMatchType', 'DeliveredMatchType', 'Impressions', 'Clicks', 'Spend', 'AveragePosition', 'Conversions']
             }

    columns = columns_map.get(report_type)

    if isinstance(date_range, list):
      # do this
      print('wha')
    elif isinstance(date_range, str):
      # Commented out: date_range = date_range
      print(date_range)
    return(date_range, report_type)
  get_keyword_report_request()
Chui Tey
  • 5,436
  • 2
  • 35
  • 44
  • 1
    It's worth noting that if you do need to assign to a variable in an outside scope, in Python 3 (which the questioner is probably using, given the parentheses around his `print` args) you can use a `nonlocal` statement. In Python 2, your only options are to wrap the value in the outer scope up in some kind of container (like a list) or make it an attribute of some object. You can mutate the contents of a non-local variable with impunity, just not rebind it (without a `nonlocal` statement). – Blckknght Sep 24 '15 at 11:58
  • @Chui Tey This was super-helpful. I have never heard of the term [Python Warts](https://wiki.python.org/moin/PythonWarts) before. This problem was driving me crazy at the time! Your examples and explanation above is great. I should have named date_range something else, or just not made it at all (I just did it thinking it wasn't being referenced mentioned from the `UnboundLocalError`). – Jarad Sep 25 '15 at 04:33