web-dev-qa-db-ja.com

PythonにはJavaの匿名内部クラスのようなものがありますか?

Javaでは、匿名の内部クラスを使用してインラインで新しいクラスを定義できます。これは、クラスの単一のメソッドのみを書き直す必要がある場合に役立ちます。

単一のメソッドのみをオーバーライドするOptionParserのサブクラスを作成するとします(たとえば、exit())。 Javaでは、次のように記述できます。

_new OptionParser () {

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

このコードは、OptionParserを拡張し、exit()メソッドのみをオーバーライドする匿名クラスを作成します。

Pythonにも同様のイディオムがありますか?このような状況で使用されるイディオムはどれですか?

41
Andrea Francia

type(name, bases, dict) 組み込み関数を使用して、その場でクラスを作成できます。例えば:

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

OptionParserは新しいスタイルのクラスではないため、基本クラスのリストにobjectを明示的に含める必要があります。

41
Joe Hildebrand

Javaは主に匿名クラスを使用して、クロージャーまたは単にコードブロックを模倣します。 Pythonではメソッドを簡単に渡すことができるので、匿名の内部クラスのように不格好な構造は必要ありません。

def printStuff():
   print "hello"

def doit(what):
   what()

doit(printStuff) 

編集:私はこれがこの特別な場合に必要なものではないことを知っています。 Javaの匿名内部クラスによる問題の最も一般的なpythonソリューションについて説明しました。

18
Joachim Sauer

これは、次の3つの方法で実行できます。

  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()
12
gnud

クラスはファーストクラスのオブジェクトなので、必要に応じてメソッドで作成できます。例えば.

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__の引数として入力します。

(編集:コードの入力エラーを修正)

11
John Fouhy

Pythonはこれを直接サポートしていません(匿名クラス)が、構文が簡潔なため、実際には必要ありません。

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

p = MyOptionParser()

唯一の欠点は、名前空間にMyOptionParserを追加することですが、John Fouhyが指摘したように、複数回実行する場合は、関数内に非表示にすることができます。

6
davidavr

Pythonには、おそらく問題を解決するためのより良い方法があります。あなたがやりたいことのより具体的な詳細を提供することができれば、それは助けになるでしょう。

たとえば、コード内の特定のポイントで呼び出されるメソッドを変更する必要がある場合は、関数をパラメーターとして渡すことでこれを行うことができます(関数は、Pythonのファーストクラスのオブジェクトであり、関数に渡すことができます)。匿名のlambda関数を作成することもできます(ただし、これらは単一の式に制限されています)。

また、pythonは非常に動的であるため、オブジェクトの作成後にオブジェクトのメソッドを変更できますobject.method1 = alternative_impl1、実際にはもう少し複雑ですが、 gnudの答え を参照してください。

5
hasen

pythonには、ラムダステートメントを使用して宣言された無名関数があります。私はそれらがあまり好きではありません-それらはあまり読みにくく、機能が制限されています。

ただし、あなたが話していることは、まったく異なるアプローチでpythonに実装される可能性があります。

class a(object):
  def meth_a(self):
    print "a"

def meth_b(obj):
  print "b"

b = a()
b.__class__.meth_a = meth_b
2
rob

私は通常、内部クラスを使用して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は機能しません。もう少し「匿名」にしたい場合は、気にしないでください。

ただし、マングリングが必要ない場合は、単一の下線付きの「プライベート」表記を使用することをお勧めします。

私の場合、必要なのは、いくつかのクラス属性を設定するための高速サブクラスと、それに続くRestSerializerクラスのクラス属性に割り当てることだけです。したがって、二重アンダースコアは「これ以上使用しない」ことを示し、次のように変更される可能性があります。他の場所で再利用し始めた場合、アンダースコアはありません。

0
g4borg

クラスはいつでも変数で非表示にできます。

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

の代わりに

 var = new ...() {};
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()
0
grepit