【问题标题】:How to declare a dictionary with inline function如何使用内联函数声明字典
【发布时间】:2012-01-14 17:55:57
【问题描述】:

我必须先正式定义一个函数,然后才能将它用作字典的元素吗?

def my_func():
  print 'my_func'

d = {
  'function': my_func
}

我宁愿定义内联函数。我只是试图输入我想要做的事情,但是 python 语法的空格策略使得在 dict 中定义内联函数变得非常困难。有没有办法做到这一点?

【问题讨论】:

  • 您能解释一下为什么要这样做吗?如果你想从函数中返回一个值,你可以使用 d['function'] = my_func() 将它放入字典中;如果你想要一个你再也不需要的功能,你可以使用lambda
  • lambda 对我不起作用,因为我需要函数中的语句。该功能并非微不足道。如果可以的话,我想避免声明函数。我想要内联声明的函数。
  • 您希望如何使用当时未定义的任何内容(不是物理文件位置)? Python 通过执行def 语句来定义一个函数。 Python 通过执行您的 d = { etc etc etc} 来定义您的 dict。
  • @JohnMachin 我还不知道 python 在底层是如何工作的。我只知道你可以用其他语言做这种事情。 python中一定没有办法。
  • 我想这样做的一个原因是为了网络。我想要一个命令处理程序字典,其中键是命令 id,值是处理该命令的函数。如果我不需要这些函数声明,我宁愿不要污染命名空间。

标签: python


【解决方案1】:

答案似乎是没有办法在python中声明一个函数内联字典定义。感谢所有花时间做出贡献的人。

【讨论】:

  • 这是最正确的答案。虽然很高兴知道在 Python 中做类似事情的五种左右的迂回方法,但除了 lambda 之外,没有其他方法可以用匿名函数来做 Javascript 中如此简单的事情,这些函数仅限于可以放在一行上的简单函数.
  • 我想指出 Python 没有像 JavaScript 那样做的原因是为了可读性,以及选择使用空格而不是括号来表示控制块的结果。直到使用 Python 多年后,我才能够轻松地理解 JavaScript 的“好的部分”。
  • 同意 JavaScript 在这部分是正确的,将一些函数声明为包含它们的对象内联是非常容易和可读的
【解决方案2】:

您真的需要字典,还是只需要getitem 访问权限?

如果是后者,那么使用一个类:

>>> class Dispatch(object):
...     def funcA(self, *args):
...         print('funcA%r' % (args,))
...     def funcB(self, *args):
...         print('funcB%r' % (args,))
...     def __getitem__(self, name):
...         return getattr(self, name)
... 
>>> d = Dispatch()
>>> 
>>> d['funcA'](1, 2, 3)
funcA(1, 2, 3)

【讨论】:

  • 如果他真的想实例化对象,这是一个好方法,希望因为它们包含一些有用的状态。这里的关键点是,您的示例没有显示,该类应该包含一些值得实例化的有用状态,否则他可以使用我的答案中描述的方法通过字符串键访问函数。
  • @DerekLitz。实用胜过纯洁。 OP的问题是关于字典中的内联函数,这非常接近python类已经是什么(即一个字典的包装器)。我的示例以可读且易于维护的形式提供了 OP 真正需要的功能。
  • 我认为你错过了我的观点。实例化上面的 d 对象没有任何意义,这会导致混乱。它在不需要的地方增加了额外的复杂性(因为你没有有意义的状态)。 d 乍一看似乎表现得像一个 dict,但实际上并非如此。您没有使用 Class 您正在使用 Dispatch 对象来提供功能,恕我直言,Dispatch 对象没有添加任何有意义的功能或简化。 getitem 应该用于通过重用常见的 Python 习惯用法来简化复杂类的使用,就像 sqlalchemy 对查询所做的那样。
  • @DerekLitz。我的 Dispatch 类与 OP 问题中的 dict 具有相同的目的:它是函数的容器(或命名空间),它还提供 getitem 访问权限。与dict 相比,它的主要优势在于它允许内联定义函数——这正是 OP 所要求的。这实际上只是一个非常常见的 python 习惯用法的简单示例。它将一个对象(OP 的字典)替换为另一个在重要的地方表现相同的对象(即getitem 访问)。
  • 是的,这就是为什么我说这是一个好方法,只要他的用法足够复杂,可以保证类的实例化。否则,我不认为这样做有什么意义。这仍然是我的观点,我还没有看到你对额外的复杂性提出任何论据。我为知识较少的用户指出了这一点,他们只会简单地获取您的代码并使用它,而不知道对于更简单的用例有更简单的方法。很明显,我喜欢你的方式,如果它被类的必要复杂性所证明是合理的。
【解决方案3】:

你可以use a decorator:

func_dict = {}

def register(func):
    func_dict[func.__name__] = func
    return func

@register
def a_func():
    pass

@register
def b_func():
    pass

func_dict 最终将使用函数的完整名称进行映射:

>>> func_dict
{'a_func': <function a_func at 0x000001F6117BC950>, 'b_func': <function b_func at 0x000001F6117BC8C8>}

您可以根据需要修改register 使用的密钥。诀窍是我们使用函数的__name__ 属性来获取适当的字符串。

【讨论】:

    【解决方案4】:

    考虑使用 lambdas,但请注意 lambdas 必须函数并且不能包含语句(参见http://docs.python.org/reference/expressions.html#lambda

    例如

    d = { 'func': lambda x: x + 1 }
    # call d['func'](2) will return 3
    

    另外,请注意,在 python2 中,print 不是函数。所以你必须这样做:

    from __future__ import print_function
    
    d = {
        'function': print
    }
    

    或改用sys.stdout.write

    d = {
        'function': sys.stdout.write
    }
    

    【讨论】:

    • 我反对“必须是函数,而不是过程”的表述。无论您使用这些术语的哪个相互矛盾的定义,都没有真正适合。唯一的限制是 lambda 的主体必须是单个表达式。
    • @delnan 感谢您的澄清。答案已编辑。我曾经将控制流(例如iffor)作为过程,从而导致歧义。
    • 感谢您对print 的关注。
    【解决方案5】:

    一些函数可以很容易地用 lambda 表达式匿名“内联”,例如:

    >>> d={'function': lambda x : x**2}
    >>> d['function'](5)
    25
    

    但对于任何半复杂的(或 using 语句),您可能应该事先定义它们。

    【讨论】:

    • 谢谢,但是 lambda 是不够的。我需要函数中的语句。
    【解决方案6】:

    没有充分的理由要使用 Python 中的字典来编写此代码。这很奇怪,而且不是命名空间函数的常用方法。

    这里适用的 Python 哲学是:

    应该有一种——最好只有一种——明显的方法。

    结合

    可读性很重要。

    这样做也使典型的 Python 用户难以理解和阅读。

    在这种情况下,字典的好处是将字符串映射到函数并在字典中命名它们,但是模块和类都已提供此功能,熟悉 Python 的人更容易理解。

    例子:

    模块方法:

    #cool.py
    
    def cool():
        print 'cool'
    

    现在像使用字典一样使用模块:

    import cool
    #cool.__dict__['cool']()
    #update - to the more correct idiom vars
    vars(cool)['cool']()
    

    类方法:

    class Cool():
        def cool():
            print 'cool'
    
    #Cool.__dict__['cool']()
    #update - to the more correct idiom vars
    vars(Cool)['cool']()
    

    在下面的评论后编辑:

    argparse 似乎很适合这个问题,所以你不必重新发明轮子。如果您决定完全自己实现它,尽管 argparse source 应该会给您一些好的方向。无论如何,以下部分似乎适用于这个用例:

    15.4.4.5。超越 sys.argv

    有时让 ArgumentParser 解析参数可能很有用 除了 sys.argv 的那些。这可以通过传递一个 parse_args() 的字符串列表。这对于在 互动提示:

    15.4.5.1。子命令¶

    ArgumentParser.add_subparsers()

    许多程序将它们的功能拆分为多个子命令,例如,svn 程序可以调用子命令 比如 svn checkout、svn update 和 svn commit。

    15.4.4.6。命名空间对象

    让 ArgumentParser 将属性分配给 一个已经存在的对象,而不是一个新的命名空间对象。这 可以通过指定 namespace= 关键字参数来实现:

    更新,这里是一个使用 argparse 的例子

    strategizer = argparse.ArgumentParser()
    strat_subs = strategizer.add_subparsers()
    math = strat_subs.add_parser('math')
    math_subs = math.add_subparsers()
    math_max = math_subs.add_parser('max')
    math_sum = math_subs.add_parser('sum')
    math_max.set_defaults(strategy=max)
    math_sum.set_defaults(strategy=sum)
    
    strategizer.parse_args('math max'.split())
    Out[46]: Namespace(strategy=<built-in function max>)
    
    strategizer.parse_args('math sum'.split())
    Out[47]: Namespace(strategy=<built-in function sum>)
    

    我想说明我推荐 argparse 的原因

    1. 主要是要求使用表示选项和子选项的字符串映射到函数。
    2. 非常简单(通过功能填充的 argparse 模块后)。
    3. 使用 Python 标准库模块。这让其他熟悉 Python 的人无需深入了解实现细节即可了解您在做什么,并且对于那些不熟悉的人来说,文档也非常完善。
    4. 开箱即用可以利用许多额外的功能(不是最好的理由!)。

    结合使用 argparse 和策略模式

    对于策略模式的简单实现,这个已经很好回答了。

    How to write Strategy Pattern in Python differently than example in Wikipedia?

    #continuing from the above example
    class MathStudent():
        def do_math(self, numbers):
            return self.strategy(numbers)
    
    
    maximus = strategizer.parse_args('math max'.split(),
                                      namespace=MathStudent())
    
    sumera = strategizer.parse_args('math sum'.split(),
                                     namespace=MathStudent())
    
    maximus.do_math([1, 2, 3])
    Out[71]: 3
    
    sumera.do_math([1, 2, 3])
    Out[72]: 6
    

    【讨论】:

    • 我真正想要在这里实现的是策略模式。我想要一个包含多个选项和功能级别的字典,所以我可以这样做:d[option1][option2](args)。但我想以最简单的方式做到这一点。在 Groovy 或 JavaScript 中,您可以在地图中内联闭包。可能我需要为我希望成为策略选项的函数创建一个类。
    • 想要做 OP 正在做的事情有很多很好的理由。这是一种非常常见的技术,称为Dynamic Dispatch
    • @ekhumoro Python 语言实现了动态调度... OP 没有实现动态调度...
    • @MatthewTaylor 我认为 argparse 虽然通常用于命令行,但会做你想做的事。
    • @DerekLitz。是的,对不起。当然我的意思是Dispatch Table
    【解决方案7】:

    内联函数的目的是模糊字典和类实例之间的区别。例如,在 javascript 中,这种技术使得编写几乎没有可重用性的控制类变得非常愉快。此外,非常有帮助的是,API 然后符合众所周知的字典协议,是不言自明的(双关语)。

    您可以在 python 中执行此操作 - 它看起来不像字典!实际上,您可以在任何范围内使用 class 关键字(即函数中的类 def,或类 def 中的类 def),它的子项可以是您要查找的字典;只需检查定义的属性,就好像它是一个 javascript 字典。

    就好像它是真实的例子:

    somedict = {
        "foo":5,
        "one_function":your method here,
        "two_function":your method here,
    }
    

    实际上是这样完成的

    class somedict:
        foo = 5
        @classmethod
        def one_method(self):
            print self.foo
            self.foo *= 2;
    
        @classmethod
        def two_method(self):
            print self.foo
    

    这样你就可以说:

    somedict.foo          #(prints 5)
    somedict.one_method() #(prints 5)
    somedict.two_method() #(prints 10)
    

    通过这种方式,您可以获得与“内联”相同的逻辑分组。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-03-08
      • 1970-01-01
      • 1970-01-01
      • 2019-07-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多