Python:将字符串转换为函数名称; getattr还是相等?

时间:2009-11-15 20:19:14

标签: python function getattr

我正在编辑PROSS.py以使用蛋白质结构的.cif文件。在现有的PROSS.py中,有以下函数(我相信如果它与任何类没有关联,它的名称是正确的吗?),只存在于.py文件中:

...
def unpack_pdb_line(line, ATOF=_atof, ATOI=_atoi, STRIP=string.strip):
...
...
def read_pdb(f, as_protein=0, as_rna=0, as_dna=0, all_models=0,
    unpack=unpack_pdb_line, atom_build=atom_build):

我正在为命令行参数添加一个optons解析器,其中一个选项是指定除unpack_pdb_line之外使用的替代方法。所以选项解析器的相关部分是:

...
parser.add_option("--un", dest="unpack_method", default="unpack_pdb_line", type="string", help="Unpack method to use. Default is unpack_pdb_line.")
...
unpack=options.unpack_method

但是,options.unpack_method是一个字符串,我需要使用与options.unpack_method中的字符串同名的函数。如何使用getattr等将字符串转换为实际的函数名?

谢谢,

5 个答案:

答案 0 :(得分:8)

通常你只需使用dict并存储(func_name, function)对:

unpack_options = { 'unpack_pdb_line' : unpack_pdb_line,
                   'some_other' : some_other_function }

unpack_function = unpack_options[options.unpack_method]

答案 1 :(得分:4)

如果您想利用Python已经代表您保留的词典(& c),我建议:

def str2fun(astr):
  module, _, function = astr.rpartition('.')
  if module:
    __import__(module)
    mod = sys.modules[module]
  else:
    mod = sys.modules['__main__']  # or whatever's the "default module"
  return getattr(mod, function)

您可能希望检查函数的签名(并捕获异常以提供更好的错误消息),例如通过inspect,但这是一个有用的通用功能。 如果某些已知函数的完整字符串名称(包括模块/包资格)难以用这种方式表达,那么很容易添加一个快捷方式字典作为后备。

注意我们不使用__import__的结果(当函数位于某个包内的模块中时它不起作用,因为__import__返回包的顶级名称...只需在导入后访问sys.modules更实用)。

答案 2 :(得分:2)

vars()["unpack_pdb_line"]()也可以。

globals()或locals()也将以类似的方式工作。

>>> def a():return 1
>>>
>>> vars()["a"]
<function a at 0x009D1230>
>>>
>>> vars()["a"]()
1
>>> locals()["a"]()
1
>>> globals()["a"]()
1

干杯,

答案 3 :(得分:1)

如果您正在接受用户的输入,为了安全起见,最好是 使用手工制作的dict,它只接受一组明确定义的可接受的用户输入:

unpack_options = { 'unpack_pdb_line' : unpack_pdb_line,
    'unpack_pdb_line2' : unpack_pdb_line2,
    } 

暂时忽略安全性,让我们顺便指出一个简单的方法 从(变量名称的字符串)到(变量名称引用的值) 是使用globals()内置字典:

unpack_function=globals()['unpack_pdb_line']

当然,只有变量unpack_pdb_line在全局命名空间中时才会起作用。

如果您需要为模块或模块获取变量,那么 你可以使用这个功能

import sys
def str_to_obj(astr):
    print('processing %s'%astr)
    try:
        return globals()[astr]
    except KeyError:
        try:
            __import__(astr)
            mod=sys.modules[astr]
            return mod
        except ImportError:
            module,_,basename=astr.rpartition('.')
            if module:
                mod=str_to_obj(module)
                return getattr(mod,basename)
            else:
                raise

您可以像这样使用它:

str_to_obj('scipy.stats')
# <module 'scipy.stats' from '/usr/lib/python2.6/dist-packages/scipy/stats/__init__.pyc'>

str_to_obj('scipy.stats.stats')
# <module 'scipy.stats.stats' from '/usr/lib/python2.6/dist-packages/scipy/stats/stats.pyc'>

str_to_obj('scipy.stats.stats.chisquare')
# <function chisquare at 0xa806844>

适用于嵌套包,模块,函数或(全局)变量。

答案 4 :(得分:1)

function = eval_dottedname(name if '.' in name else "%s.%s" % (__name__, name))

eval_dottedname()

def eval_dottedname(dottedname):
    """
    >>> eval_dottedname("os.path.join") #doctest: +ELLIPSIS
    <function join at 0x...>
    >>> eval_dottedname("sys.exit") #doctest: +ELLIPSIS
    <built-in function exit>
    >>> eval_dottedname("sys") #doctest: +ELLIPSIS
    <module 'sys' (built-in)>
    """
    return reduce(getattr, dottedname.split(".")[1:],
                  __import__(dottedname.partition(".")[0]))

eval_dottedname()是所有答案中唯一支持其中包含多个点的任意名称的答案,例如`'datetime.datetime.now'。虽然它不适用于需要导入的嵌套模块,但我甚至不记得stdlib中这样一个模块的例子。