python:以编程方式为便捷类生成方法

时间:2014-10-28 02:32:25

标签: python metaprogramming

所以我写了一个包含一堆函数的模块,可以轻松地与子进程交互。此子进程具有一大堆设置,可让您更改其格式和行为方式。我意识到有一个便利类可以用作处理程序来存储你喜欢使用的设置并将它们传递给模块级函数。这是我正在测试的示例代码:

import inspect

class MyHandler(object):

    def __init__(self):
        self.format_string='class format string'
        self.database='class database'
        self.mode = "class mode"

    def rename(self, *args, **kwargs):
        self._pass_to_function(rename, *args, **kwargs)

    def _pass_to_function(self, function, *overrided_args, **overrided_kwargs):
        #  get the function's remaining arguments with the inspect module
        functon_kwargs = inspect.getargspec(function)[0][len(overrided_args):]

        handler_vars = vars(self)
        kwargs_to_pass = {}
        for arg in functon_kwargs:
            if arg in handler_vars:
                kwargs_to_pass[arg] = handler_vars[arg]
        for arg in overrided_kwargs:
            kwargs_to_pass[arg] = overrided_kwargs[arg]

        return function(*overrided_args, **kwargs_to_pass)


def rename(targets, format_string=None, database=None, mode=None,
           not_in_class='None'):
    print 'targets = {}'.format(targets)
    print 'format_string = {}'.format(format_string)
    print 'database = {}'.format(database)
    print 'mode = {}'.format(mode)
    print 'not_in_class = {}\n'.format(not_in_class)
    return

我喜欢这个解决方案的方法是它使用存储在类中的属性,但如果你想要一个具有不同设置的一次性,只需将它们添加到方法调用中就可以轻松地覆盖它们。为此,我将_pass_to_function作为一种包装函数来解析和填充所需的设置和覆盖。这是它的外观:

>>> import argstest
>>> argstest.rename('some_file.avi', database='some database')
targets = some_file.avi
format_string = None
database = some database
mode = None
not_in_class = None

>>> tst = argstest.MyHandler()
>>> tst.rename('some_file.avi')
targets = some_file.avi
format_string = class format string
database = class database
mode = class mode
not_in_class = None

>>> tst.rename('some_file.avi', 'one off format string', not_in_class=True)
targets = some_file.avi
format_string = one off format string
database = class database
mode = class mode
not_in_class = True

现在在我的实际模块中,我有几十个我想从处理程序类访问的模块级函数。理想情况下,它们会根据模块中的功能自动生成。看到所有方法只是将所有方法都传递给_pass_to_function我觉得这应该不是很困难,但我很难弄清楚具体如何。

我已经阅读了使用type生成元类的内容,但我不知道如何在这种情况下使用它。我没有看到如何使用type?我应该使用某种模块级脚本来添加setattr函数吗?我正在做更好/更清晰的做事方式吗?

任何和所有建议都将不胜感激。

1 个答案:

答案 0 :(得分:0)

好的,我想我现在已回答了我自己的问题。这就是模块的外观:

import inspect
import sys
from types import MethodType


class MyHandler(object):

    def __init__(self):
        self.format_string = 'class format string'
        self.database = 'class database'
        self.mode = "class mode"
        self._populate_methods()

    def _populate_methods(self):
        to_add = inspect.getmembers(sys.modules[__name__], inspect.isfunction)
        to_add = [x[0] for x in to_add if not x[0].startswith('_')]
        for func_name in to_add:
            func = getattr(sys.modules[__name__], func_name)  # strings to functions
            self._add_function_as_method(func_name, func)

    def _add_function_as_method(self, func_name, func):
        def f(self, *args, **kwargs):  #  the template for the method we'll add
            return self._pass_to_function(func, *args, **kwargs)
        setattr(MyHandler, func_name, MethodType(f, None, MyHandler))

    def _pass_to_function(self, function, *overrided_args, **overrided_kwargs):
        functon_kwargs = inspect.getargspec(function)[0][len(overrided_args):]
        handler_vars = vars(self)
        kwargs_to_pass = {}

        for arg in functon_kwargs:
            if arg in handler_vars:
                kwargs_to_pass[arg] = handler_vars[arg]
        for arg in overrided_kwargs:
            kwargs_to_pass[arg] = overrided_kwargs[arg]

        return function(*overrided_args, **kwargs_to_pass)


def rename(targets, format_string=None, database=None, mode=None,
           not_in_class='None'):
    print 'targets = {}'.format(targets)
    print 'format_string = {}'.format(format_string)
    print 'database = {}'.format(database)
    print 'mode = {}'.format(mode)
    print 'not_in_class = {}\n'.format(not_in_class)
    return


def something_else():
    print "this function should become a method"


def _not_a_member():
    print "this function should not become a method"

我添加了_populate_methods_add_function_as_method成员函数。 _populate_methods函数获取模块中所有“公共”函数的名称,将它们取消引用到它们的函数并通过_add_function_as_method传递每个函数。所有这个方法都是使用内部函数来捕获参数并将它们发送到_pass_to_function,并使用setattr将该函数设置为方法。

所以它有效,但我仍然想知道是否有更清晰或更直接的方法来完成这项工作。如果有人能插手,我会非常感激。