【问题标题】:python: generating methods for a convenience class programaticallypython:以编程方式为便利类生成方法
【发布时间】:2014-10-28 02:32:25
【问题描述】:

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

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 的功能吗?我正在做的事情是更好/更清晰的方式吗?

我们将不胜感激。

【问题讨论】:

  • 你想做什么?
  • 我正在尝试根据模块中的其他函数为 MyHandler 生成成员函数。这就是上面的代码所做的,但它做起来有些不雅。
  • 好的。我仍在尝试了解您想要这样做的为什么。您是否尝试创建可在类和便利模块级函数中访问的相同函数?看看 Python 的 logger 模块就是一个例子。
  • 如果你看一下 rename 函数,你会发现它需要 4 个参数。在我正在编写的模块中,有多达 17 个函数。单个用户通常只需要 1 或 2 个。但其他用户将有几组许多参数,他们在对同一函数的不同调用中一遍又一遍地使用.拥有一个将保存这些参数并在适当的地方使用它们的类会很方便,因此您只需设置一次。我想自动生成它们,以便对模块级函数的更改反映在类中,而不必跟踪所有内容。

标签: python metaprogramming


【解决方案1】:

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

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 将该函数设置为方法。

所以它可以工作,但我仍然想知道是否没有更清晰或更直接的方法来完成这项工作。如果有人能插话,我将不胜感激。

【讨论】:

    猜你喜欢
    • 2012-01-08
    • 2011-04-27
    • 1970-01-01
    • 2018-04-17
    • 1970-01-01
    • 2017-04-20
    • 2010-09-06
    • 2018-06-23
    • 2018-08-14
    相关资源
    最近更新 更多