45

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

假设您要创建一个OptionParser仅覆盖单个方法的子类(例如exit())。在 Java 中,您可以编写如下内容:

new OptionParser () {

    public void exit() {
        // body of the method
    }
};

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

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

4

11 回答 11

50

您可以使用type(name, bases, dict)内置函数动态创建类。例如:

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

由于 OptionParser 不是新式类,因此您必须明确地包含object在基类列表中。

于 2010-10-12T13:53:02.040 回答
17

Java 主要使用匿名类来模拟闭包或简单的代码块。由于在 Python 中您可以轻松地传递方法,因此不需要像匿名内部类那样笨拙的构造:

def printStuff():
   print "hello"

def doit(what):
   what()

doit(printStuff) 

编辑:我知道这不是这种特殊情况所需要的。我刚刚描述了Java中匿名内部类最常见的问题的最常见python解决方案。

于 2008-12-10T23:35:43.170 回答
14

您可以通过三种方式完成此操作:

  1. 适当的子类(当然)
  2. 您使用对象作为参数调用的自定义方法
  3. (您可能想要什么)- 向对象添加新方法(或替换现有方法)。

选项 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()
于 2008-12-11T00:01:54.643 回答
12

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

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__.

(编辑:修复代码中的输入错误)

于 2008-12-11T00:05:13.610 回答
6

Python 不直接支持这一点(匿名类),但由于其简洁的语法,它并不是真正必要的:

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

p = MyOptionParser()

唯一的缺点是您将 MyOptionParser 添加到您的命名空间,但正如 John Fouhy 指出的那样,如果您要多次执行此操作,您可以将其隐藏在函数中。

于 2008-12-11T15:06:37.393 回答
5

Python 可能有更好的方法来解决您的问题。如果您可以提供有关您想要做什么的更具体的详细信息,那将有所帮助。

例如,如果您需要更改在代码中的特定点调用的方法,您可以通过将函数作为参数传递来实现(函数是 python 中的第一类对象,您可以将它们传递给函数等)。您还可以创建匿名lambda函数(但它们仅限于单个表达式)。

此外,由于 python 非常动态,您可以在创建对象后更改对象的方法object.method1 = alternative_impl1,虽然它实际上有点复杂,请参阅gnud 的答案

于 2008-12-10T23:54:47.560 回答
2

在 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
于 2008-12-10T23:49:53.593 回答
0

您始终可以按变量隐藏类:

 class var(...):
     pass
 var = var()

代替

 var = new ...() {};
于 2011-05-30T10:28:57.763 回答
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()
于 2019-01-23T06:21:41.737 回答
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()

当我使用双下划线时,这将“修改”的想法与内部类混合在一起,从外部您仍然可以使用SomeSerializer._SomeSerializer__Paginator, 和子类访问内部类,但是 SomeSerializer.__Paginator 将不起作用,这可能是也可能不是你的愿望您希望它更“匿名”。

但是,如果您不需要修改,我建议使用带有单个下划线的“私有”表示法。

In my case, all I need is a fast subclass to set some class attributes, followed up by assigning it to the class attribute of my RestSerializer class, so the double underscore would denote to "not use it at all further" and might change to no underscores, if I start reusing it elsewhere.

于 2019-04-10T13:36:56.553 回答
0

Being perverse, you could use the throwaway name _ for the derived class name:

class _(OptionParser):

    def exit(self):
        pass  # your override impl
于 2021-01-25T23:24:20.687 回答