Есть ли в Python что-то вроде анонимных внутренних классов Java? - PullRequest
39 голосов
/ 11 декабря 2008

В Java вы можете определить новый встроенный класс, используя анонимные внутренние классы. Это полезно, когда вам нужно переписать только один метод класса.

Предположим, что вы хотите создать подкласс OptionParser, который переопределяет только один метод (например, exit()). В Java вы можете написать что-то вроде этого:

new OptionParser () {

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

Этот фрагмент кода создает анонимный класс, который расширяет OptionParser и переопределяет только метод exit().

Есть ли в Python похожая идиома? Какая идиома используется в этих обстоятельствах?

Ответы [ 10 ]

38 голосов
/ 12 октября 2010

Вы можете использовать встроенную функцию type(name, bases, dict) для создания классов на лету. Например:

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

Поскольку OptionParser не является классом нового стиля, вы должны явно включить object в список базовых классов.

18 голосов
/ 11 декабря 2008

Java использует анонимные классы в основном для имитации замыканий или просто блоков кода. Так как в Python вы можете легко обойти методы, нет необходимости создавать такую ​​же неуклюжую конструкцию, как анонимные внутренние классы:

def printStuff():
   print "hello"

def doit(what):
   what()

doit(printStuff) 

Редактировать: я знаю, что это не то, что нужно в этом особом случае. Я только что описал наиболее распространенное решение проблемы на Python, чаще всего с помощью анонимных внутренних классов в Java.

11 голосов
/ 11 декабря 2008

Ну, классы - это объекты первого класса, так что вы можете создавать их в методах, если хотите. например,

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 голосов
/ 11 декабря 2008

Вы можете сделать это тремя способами:

  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()
6 голосов
/ 11 декабря 2008

Python не поддерживает это напрямую (анонимные классы), но из-за его краткого синтаксиса это на самом деле не нужно:

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

p = MyOptionParser()

Единственным недостатком является то, что вы добавляете MyOptionParser в свое пространство имен, но, как отметил Джон Фухи, вы можете скрыть это внутри функции, если собираетесь делать это несколько раз.

5 голосов
/ 11 декабря 2008

В Python, вероятно, есть лучшие способы решения вашей проблемы. Если бы вы могли предоставить более подробную информацию о том, что вы хотите сделать, это помогло бы.

Например, если вам нужно изменить метод, вызываемый в определенной точке кода, вы можете сделать это, передав функцию в качестве параметра (функции - это объекты первого класса в python, вы можете передать их функциям и т. Д. ). Вы также можете создавать анонимные lambda функции (но они ограничены одним выражением).

Кроме того, поскольку python очень динамичен, вы можете изменить методы объекта после того, как он был создан object.method1 = alternative_impl1, хотя на самом деле он немного сложнее, см. ответ gnud

2 голосов
/ 11 декабря 2008

В 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
0 голосов
/ 10 апреля 2019

Я делаю это в 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. может не быть вашим желанием, если вы хотите, чтобы он был немного более анонимным.

Однако я предлагаю использовать "частную" нотацию с одним подчеркиванием, если вам не нужно искажение.

В моем случае все, что мне нужно, это быстрый подкласс для установки некоторых атрибутов класса, за которым следует присвоение его атрибуту класса моего класса RestSerializer, поэтому двойное подчеркивание будет означать «не использовать его вообще» и может измениться на подчеркивание, если я начну использовать его в другом месте.

0 голосов
/ 23 января 2019

Это то, что вы будете делать в 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 голосов
/ 30 мая 2011

Вы всегда можете скрыть класс по переменным:

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

вместо

 var = new ...() {};
...