Oh, happy day! eval() has been expunged from my program!! I will now continue from where I left off earlier.
On Sun, Mar 24, 2019 at 12:22 AM Cameron Simpson <c...@cskk.id.au> wrote: > > On 23Mar2019 22:15, boB Stepp <robertvst...@gmail.com> wrote: > > The lambda is just a single line function definition, and doesn't get a > function name. > So your get_input name now accepts a "date_constructor" parameter and > you would change the input step from this: > > try: > identifier = int(input(input_prompt)) > if date_value_err_ck: > date(*date_value_err_ck) > except ValueError: > > to this: > > try: > value = int(input(input_prompt)) > date = date_constructor(value) > except ValueError: You could not know this from the code I showed (As I pared it down to illustrate my problem.), but I am also using get_input() to pull in from the user non-date values, too, but they are also all integers: pages_read and total_pages_to_read. Because of this, for these latter two items, "date_value_err_ck" will be assigned "None", which is why I have the "if date_value_err_ck:". > So you're passing in a function to make the date, and only calling it > once you've read in the value. > > This solves your use of goal_year before it is defined in 2 ways: it > doesn't use the value before the call to get_input and also the name > "goal_year" in the constructor function is local to that function - it > may as well be called "year". > > Now to your conditions. You have: > > 'conditions': [ > ('goal_year < date.today().year', > "Have you invented a time machine? If not, please enter a" > " year that makes more sense!"), > ('goal_year >= date.today().year + 100', > "Have you discovered the secret to eternal life? And how" > " long is this book anyway? Please enter a year that" > " makes more sense.")]} > > These seem to be tests for bad values (personally I tend to write tests > for good values, but this is an arbitrary choice). These should also be > written as lambdas: I just like writing corny error messages in response to bad values. ~(:>)) > But wait, there's more! > > After you have phase 1 complete (inputting goal_year) you then want > goal_month. > > Let me introduce you to the closure: > > For the goal_year you have the year-based constructor using only the > input value: > > 'date_constructor': lambda goal_year: datetime.date(goal_year, 1, 1), > > in your setup dict. For the goal_month you want both the goal_year and > the input value. So... > > 'date_constructor': lambda goal_month: datetime.date(goal_year, > goal_month, 1), > > But, I hear you ask, I'm not passing in the goal_year to the get_input() > for the month value! Indeed, your code will look like this: > > goal_year_params = { > .... > 'date_constructor': lambda goal_year: datetime.date(goal_year, 1, 1), > .... > } > goal_year = get_input(**goal_year_params) > goal_month_params = { > .... > 'date_constructor': lambda goal_month: datetime.date(goal_year, > goal_month, 1), > .... > } > goal_month = get_input(**goal_month_params) > > That's your plan, yes? You understand me perfectly! > Well, when you define the goal_month constructor lambda function, > goal_year _is_ a local variable. And _because_ it is not a parameter of > the lambda, the lambda finds it in the scope where it was defined: the > scope at the bottom of your programme. > > This is called a "closure": functions has access to the scope they are > define in for identifiers not locally defined (eg in the parameters or > the function local variables - which are those assigned to in the code). This is so cool and useful! I recall seeing this mentioned for nested functions, but did not know it worked for lambdas as well. This has been incredibly helpful and educational! Many thanks!! -- boB _______________________________________________ Tutor maillist - Tutor@python.org To unsubscribe or change subscription options: https://mail.python.org/mailman/listinfo/tutor