Python 是否有类似 Java 的匿名内类的东西?

46 人关注

在Java中,你可以使用匿名的内部类来定义一个新的内联类。当你只需要重写类中的一个方法时,这很有用。

假设你想创建一个 OptionParser 的子类,只重写一个方法(例如 exit() )。在Java中,你可以这样写。

new OptionParser () {
    public void exit() {
        // body of the method

这段代码创建了一个匿名类,它扩展了OptionParser,并且只覆盖了exit()方法。

Python中也有类似的成语?在这些情况下使用的是哪个成语?

python
class
anonymous-class
Andrea Francia
Andrea Francia
发布于 2008-12-11
11 个回答
Joe Hildebrand
Joe Hildebrand
发布于 2021-01-26
已采纳
0 人赞同

你可以使用 type(name, bases, dict) 的内置函数,以在飞行中创建类。 比如说。

op = type("MyOptionParser", (OptionParser,object), {"foo": lambda self: "foo" })
op().foo()

由于OptionParser不是一个新式的类,你必须明确地将object列入基类的列表中。

Vlad
This is the proper 内联类定义/创建匿名类的方法(引自上述引用的手册页面。"这本质上是类声明的一种动态形式");"公认的答案 "应该指向它。
我不明白这个{"foo": lambda self:"foo" }。它应该是返回自己吗?
点击进入文档的链接。 "dict字典是包含类主体定义的命名空间,成为 dict 属性"。 所以 "foo "是一个方法的名字,它只接受 "self "作为参数。 这个方法恰好可以返回字符串 "foo"。
这是一个动态类的例子,不是匿名的。
Joachim Sauer
Joachim Sauer
发布于 2021-01-26
0 人赞同

Java 使用匿名类主要是为了模仿闭包或简单的代码块。因为在Python中你可以很容易地传递方法,所以不需要像匿名内部类这样笨重的结构。

def printStuff():
   print "hello"
def doit(what):
   what()
doit(printStuff) 

编辑:我知道在这种特殊情况下不需要这样做。我只是描述了Java中匿名内类最常见的python解决方案。

-1 我想对optparse.OptionParser()进行子类化,而不是传递方法引用。
在这种情况下,写一个真正的子类有什么问题呢?我只是在说明匿名内类的最常见的用途。
+1:匿名内部类是 Java 减轻复杂语法的一种方式。 Python已经有简单的语法,不需要匿名类来减轻负担。
我知道Python "更好",但答案是关于传递方法引用,而问题是关于子类和重写一个方法。
我不会说Python更好。每种语言都有其优势。传递可调用的代码在 Python 中比在 Java 中更容易做到。我知道你在寻找别的东西,我把我的答案保留在这里,以防别人发现你的问题并寻找我提供的东西。
gnud
gnud
发布于 2021-01-26
0 人赞同

你可以通过三种方式实现这一目标。

  • Proper subclass (of course)
  • a custom method that you invoke with the object as an argument
  • (what you probably want) -- adding a new method to an object (or replacing an existing one).
  • 选项3的例子(已编辑,删除了 "新 "模块的使用 -- 它已被废弃,我不知道)。

    import types
    class someclass(object):
        val = "Value"
        def some_method(self):
            print self.val
    def some_method_upper(self):
        print self.val.upper()
    obj = someclass()
    obj.some_method()
    obj.some_method = types.MethodType(some_method_upper, obj)
    obj.some_method()
        
    如果不做一个命名的内层类并将其实例化(为了可读性,这可能更可取),选项3似乎是一个很好的方法来做这个。
    gnud
    是的 -- 这就是为什么我写了 "你可能想要什么"。我只是把它放在最后,因为这样帖子的文字更流畅 -- 而且我想列举出各种选择。
    rob
    你为什么要使用new? 它已经被废弃很久了
    因为我们正在覆盖一个先前定义的方法,而且新的临时命名的方法(some_method_upper)从未被使用过,为什么不使用lambda并使其内联,因为这将与匿名内联类更加一致。
    不确定,但可能是这样的:obj.some_method = types.MethodType(lambda self: print self.val.upper(), obj)
    0 人赞同

    好吧,类是第一类对象,所以如果你愿意,你可以在方法中创建它们。 例如:

    from optparse import OptionParser
    def make_custom_op(i):
      class MyOP(OptionParser):
        def exit(self):
          print 'custom exit called', i
      return MyOP
    custom_op_class = make_custom_op(3)
    custom_op = custom_op_class()
    custom_op.exit()          # prints 'custom exit called 3'
    dir(custom_op)            # shows all the regular attributes of an OptionParser
    

    但是,真的,为什么不直接在正常水平上定义这个类呢? 如果你需要定制它,把定制的内容作为参数放到__init__中。

    (edit: fixed typing errors in code)

    谢谢,但我不明白custom_op = custom_op_class()这一行。 它应该被删除吗?
    make_custom_op()返回一个类。 你必须调用这个类来创建一个实例,这就是这一行的作用。 然而,在我的代码中,最后两行有几个错误,我现在已经修复了,以防它们使你感到困惑。
    这是一个简单但强大的技术,如果使用得当(而不仅仅是作为一个黑客),可以使代码非常优雅。
    davidavr
    davidavr
    发布于 2021-01-26
    0 人赞同

    Python 并不直接支持这一点 (匿名类),但由于其简洁的语法,其实并没有必要。

    class MyOptionParser(OptionParser):
        def exit(self, status=0, msg=None):
            # body of method
    p = MyOptionParser()
    

    唯一的缺点是你把MyOptionParser添加到你的命名空间,但正如John Fouhy所指出的,如果你要多次这样做,你可以把它隐藏在一个函数里面。

    hasen
    hasen
    发布于 2021-01-26
    0 人赞同

    Python 可能有更好的方法来解决你的问题。如果你能提供你想做什么的更具体的细节,会有帮助。

    例如,如果你需要改变代码中某个特定点所调用的方法,你可以通过传递函数作为参数来实现(函数在python中是第一类对象,你可以将其传递给函数等)。你也可以创建匿名的 lambda 函数(但它们被限制在一个表达式上)。

    另外,由于Python是非常动态的,你可以在一个对象被创建后改变它的方法 object.method1 = alternative_impl1 ,尽管它实际上有点复杂,参见 gnud的回答

    gnud
    实际上,你不能以这种方式替换方法 -- 关于正确的方式,请看我的答案。
    rob
    rob
    发布于 2021-01-26
    0 人赞同

    在Python中,你有匿名函数,用lambda语句声明。我不太喜欢它们--它们的可读性不高,而且功能有限。

    然而,你所说的可能是在python中以完全不同的方式实现的。

    class a(object):
      def meth_a(self):
        print "a"
    def meth_b(obj):
      print "b"
    b = a()
    b.__class__.meth_a = meth_b
        
    gnud
    Right track, but this does not work. File "t", line 12, in <module> b.meth_a() TypeError: meth_b() takes exactly 1 argument (0 given)
    rob
    对:要改变一个对象的方法,你必须把它改成它的元类。我已经更新了代码。
    b.__class__.meth_a = meth_b 这段代码是影响所有基于 "a "类的对象还是只影响 "b "类的对象?
    最好把这个问题作为一个单独的问题来问,这样答案就会得到投票。
    rob
    我认为这个问题可以通过简单地在交互式shell上玩耍来得到更好的回答。这也是Python的主要优势之一:对某一语言特性有疑问,可以在sjhell中玩一玩,看看它是否有效。
    Maciej Piechotka
    Maciej Piechotka
    发布于 2021-01-26
    0 人赞同

    你总是可以通过变量来隐藏类。

     class var(...):
     var = var()
    
     var = new ...() {};
        
    grepit
    grepit
    发布于 2021-01-26
    0 人赞同

    这是你在Python 3.7中要做的事情

    #!/usr/bin/env python3
    class ExmapleClass:
        def exit(self):
            print('this should NOT print since we are going to override')
    ExmapleClass= type('', (ExmapleClass,), {'exit': lambda self: print('you should see this printed only')})()
    ExmapleClass.exit()
        
    g4borg
    g4borg
    发布于 2021-01-26
    0 人赞同

    我在Python3中这样做,通常是用内部类来做

    class SomeSerializer():
        class __Paginator(Paginator):
            page_size = 10
        # defining it for e.g. Rest:
        pagination_class = __Paginator
        # you could also be accessing it to e.g. create an instance via method:
        def get_paginator(self):
            return self.__Paginator()
    

    因为我使用了双下划线,这混合了 "mangling "和内部类的想法,从外部你仍然可以用SomeSerializer._SomeSerializer__Paginator访问内部类,也可以访问子类,但是SomeSerializer.__Paginator将不工作,如果你希望它更 "匿名",这可能是你的愿望,也可能不是。

    但是我建议使用 "private "符号,如果你不需要混用的话,就用单下划线。

    在我的例子中,我所需要的是一个快速子类来设置一些类属性,然后把它分配给我的RestSerializer类的类属性,所以双下划线将表示 "不再使用它",如果我开始在其他地方重复使用它,可能会改变为没有下划线的。

    otterrisk
    otterrisk
    发布于 2021-01-26
    0 人赞同

    作为倒行逆施,你可以使用弃用的名字 _ 作为派生类的名字。