Similar structures w/ different operations: Would this be a job for decorator functions?

时间:2018-08-22 13:42:41

标签: python python-3.x python-decorators

I'm putting together something that randomly changes parameters in math problems. As it is now, each problem is its own function—but these functions are very similar. Each of them takes some unformatted text...

Sally has {num apples} apples. Billy takes {num_taken}. How many apples does Sally have?

(... with the possibility of other arguments in the future.) And each of them returns formatted text. The difference is in what they do in between, and the number of arguments that format() needs in order to appropriately format the unformatted text. Some of them them generate the equations of planes, other just have random numbers, while others might actually need to perform certain operations on certain mathematical structures.

Eventually, too, each problem-function will also generate an answer (and return a dictionary instead of just text).

Here is an example:

# Problem 41
def p41(unformatted_text):
    plane_1 = math_structures.plane()
    plane_2 = math_structures.plane()
    point = math_structures.vector()
    formatted_text = unformatted_text.format(
            plane_1=plane_1.get_eqn(), 
            plane_2=plane_2.get_eqn(), 
            point=point.get_eqn()
    )
    return formatted_text

# Problem 52
def p52(unformatted_text):
    num_prof = random.randrange(5, 25)
    num_teaching = random.randrange(2,5)
    num_courses = num_prof * num_teaching

    formatted_text = unformatted_text.format(
            num_prof=num_prof,
            num_teaching=num_teaching,
            num_courses=num_courses
    )
    return formatted_text

# Problem 54
def p54(unformatted_text):
    range_1 = math_structures.interval()
    range_2 = math_structures.interval()
    relational = math_structures.random_relational()

    formatted_text = unformatted_text.format(
            range_1=range_1.get_eqn(),
            range_2=range_2.get_eqn(),
            relational=relational
    )
    return formatted_text

Whenever there's a similarity in structure like this, there's something that can be done to make the code more efficient. I've never had the occasion to use decorators before, but I think this might be one. I would create a decorator which takes two arguments: some function and some unformatted text. The inner-function would spit out a dictionary of some numbers or equations or whatever. The decorated-function would format the text with this dictionary, and spit out the formatted text:

def decorator(math_function):
    def decorated_function(unformatted_text, *og_args, **og_kwargs):
        formatting_arguments = math_function(*og_args, **og_kwargs)
        formatted_text = unformatted_text.format(**formatting_arguments)

        return formatted_text

And then I could have:

@decorator
def p41():
    plane_1 = math_structures.plane()
    plane_2 = math_structures.plane()
    point = math_structures.vector()
    format_dict = {
        'plane_1':plane_1,
        'plane_2':plane_2,
        'point':point
    }
    return format_dict

@decorator
def p52():
    num_prof = random.randrange(5, 25)
    num_teaching = random.randrange(2,5)
    num_courses = num_prof * num_teaching

    format_dict = {
        'num_prof':num_prof,
        'num_teaching':num_teaching,
        'num_courses':num_courses
    }
    return format_dict

Does this all sound about right? (Now that I see I have to put @decorator in front of every problem definition, I suspect there's something even more efficient. Maybe directly feed the function to the decorator when the text needs to be generated, instead of when things are defined?

text_gen.py:

import random
import problems
from decorators import decorator
problem_list = ['p41', 'p52', 'p54']
while True:
    input('Press enter to see a problem...')
    problem = random.randrange(0, len(problem_list))
    decorator(getattr(problems, problem_list[problem]))

Basically, I'd like to know if I'm on the right track, if I'm misunderstanding anything, or if there's a better way of doing this. :)

1 个答案:

答案 0 :(得分:1)

您可以做装饰器,但是我认为您想要的实际上更简单。您正在使用装饰器来更改功能,但是您也可以轻松编写用于创建该功能的功能。然后将这些问题存储在列表中以进行随机访问(再次,功能不是特殊的,不需要getargs等)。

# problem function creation
def create_from_dict(**kwargs):
    """Creates simple problems with independent arguments"""
    def format(string):
         format_args = dict( (key,val()) for (key,val) in kwargs.items())
         return string.format(**format_args)
    return format 

def create_from_function(func):
    """Creates more complex problems with dependent arguments"""
    def format(string):
         format_args = func()
         return string.format(**format_args)
    return format

# make my problems
problems = []
problems.append(create_from_dict(plane_1=lambda : math_structure.plane(),
                                 plane_2=lambda : math_structure.plane(),
                                 point=lambda : math_structures.vector()))

def p52():
    num_prof = random.randrange(5, 25)
    num_teaching = random.randrange(2,5)
    num_courses = num_prof * num_teaching

    format_dict = {
       'num_prof':num_prof,
       'num_teaching':num_teaching,
       'num_courses':num_courses
    }
    return format_dict
problems.append(create_from_function(p52))


while True:
    input('press enter to see a problem...')
    problem = random.choice(problems)