6

我正在编辑 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 等将字符串转换为实际的函数名?

谢谢,

保罗

4

5 回答 5

8

通常你只使用一个字典和存储(func_name, function)对:

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

unpack_function = unpack_options[options.unpack_method]
于 2009-11-15T20:24:01.897 回答
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)

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

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

于 2009-11-15T20:50:31.710 回答
2

如果您从用户那里获取输入,为了安全起见,最好使用手工制作的 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位于全局命名空间中时才有效。

如果你需要进入一个模块的packgae,或者一个变量的模块,那么你可以使用这个函数

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>

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

于 2009-11-15T20:48:37.357 回答
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

干杯,

于 2009-11-19T05:51:37.637 回答
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 中此类模块的示例。

于 2009-11-15T22:14:53.530 回答