Есть ли в Python что-то вроде анонимных внутренних классов Java?

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

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

new OptionParser () {

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

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

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

10.12.2008 23:26:57
10 ОТВЕТОВ
РЕШЕНИЕ

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

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

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

42
12.10.2010 13:53:02
Это правильный способ встроить определения классов / создать анонимные классы (цитата из цитируемой выше страницы руководства: «По сути, это динамическая форма выражения класса»); «принятый ответ» должен указывать на это.
Vlad 13.03.2012 00:47:47
Я не понимаю этого {"foo": лямбда-сам: "foo"}. Это должно возвращаться?
zakdances 7.03.2013 16:10:01
Нажмите на ссылку на документацию. «Словарь dict - это пространство имен, содержащее определения для тела класса, и становится атрибутом dict ». Таким образом, «foo» - это имя метода, который принимает в качестве параметра только «себя». Этот метод возвращает строку «foo».
Joe Hildebrand 7.03.2013 20:51:05
Это пример динамического класса, а не анонимного.
GuSuku 2.10.2019 20:35:23

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

def printStuff():
   print "hello"

def doit(what):
   what()

doit(printStuff) 

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

18
11.12.2008 22:09:01
-1 Я хочу, чтобы подкласс optparse.OptionParser () не передавал ссылки на методы.
Andrea Francia 10.12.2008 23:47:22
В таком случае, в чем проблема с написанием реального подкласса? Я просто иллюстрировал наиболее распространенное использование анонимных внутренних классов.
Joachim Sauer 10.12.2008 23:53:22
+1: анонимные внутренние классы - это способ для Java осветить сложный синтаксис. Python уже имеет простой синтаксис и не нуждается в анонимных классах для облегчения нагрузки.
S.Lott 11.12.2008 12:32:43
Я знаю, что Python «лучше», но ответ заключается в передаче ссылок на методы, в то время как вопрос заключается в создании подклассов и переопределении одного метода.
Andrea Francia 11.12.2008 16:18:30
Я бы не сказал, что Python лучше. У каждого языка есть своя сила. Передача вызываемого кода проще в Python, чем в Java. И я знаю, что вы ищете что-то еще, я оставляю здесь свой ответ на тот случай, если кто-то еще найдет ваш вопрос и ищет то, что я предоставляю.
Joachim Sauer 12.12.2008 12:02:02

В 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
10.12.2008 23:58:44
Правильный трек, но это не работает. Файл "t", строка 12, в <module> b.meth_a () TypeError: meth_b () принимает ровно 1 аргумент (задано 0)
gnud 10.12.2008 23:52:35
Справа: чтобы изменить метод объекта, вы должны изменить его на метакласс. Я обновил код.
rob 11.12.2008 00:18:49
b .__ class __. meth_a = meth_b Влияет ли этот код на все объекты, основанные на классе «a», или только на объект «b»?
Andrea Francia 11.12.2008 09:13:05
Лучше всего задать это отдельным вопросом, чтобы за ответы набрали голоса
James Hopkin 11.12.2008 09:49:56
Я думаю, что на вопрос можно ответить гораздо лучше, просто сыграв в интерактивную оболочку. Это одно из главных преимуществ для Python: есть сомнения или вопросы по поводу языковой функции? хорошо, поиграйте с ним в sjhell и посмотрите, действительно ли это.
rob 11.12.2008 18:18:40

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

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

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

5
23.05.2017 12:16:52
Вы на самом деле не можете заменить методы таким образом - посмотрите мой ответ для правильного пути.
gnud 11.12.2008 00:11:08

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

  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
11.12.2008 01:18:13
За исключением создания именованного внутреннего класса и его создания (что может быть предпочтительнее для удобочитаемости), вариант 3 кажется хорошим способом сделать это.
Greg Case 11.12.2008 00:14:28
да - вот почему я написал "Что вы, вероятно, хотите". Я просто назвал это последним, потому что таким образом текст сообщения течет лучше - и я хотел перечислить варианты.
gnud 11.12.2008 00:19:16
почему вы используете новый? это устарело довольно долгое время
rob 11.12.2008 00:45:25
поскольку мы перезаписываем ранее определенный метод, а новый метод с временным именем (some_method_upper) никогда не используется, почему бы не использовать лямбду и сделать ее встроенной, поскольку она будет более согласованной с анонимными встроенными классами.
fuentesjr 11.12.2008 02:28:57
не уверен, но может быть что-то вроде: obj.some_method = types.MethodType (лямбда-сам: print self.val.upper (), obj)
fuentesjr 11.12.2008 02:31:37

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

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.12.2008 00:58:41
Спасибо, но я не понимаю строку custom_op = custom_op_class (). Должен ли он быть удален?
Andrea Francia 11.12.2008 00:12:55
make_custom_op () возвращает класс. Вы должны вызвать класс для создания экземпляра, что и делает эта строка. Однако в последних двух строках в моем коде было несколько ошибок, которые я сейчас исправил на случай, если они вас смущают.
John Fouhy 11.12.2008 00:59:57
Это простой, но мощный метод, который при правильном использовании (не просто как взлом) может создать очень элегантный код.
Daniel Naab 11.12.2008 05:43:29

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

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

p = MyOptionParser()

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

6
11.12.2008 15:06:37

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

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

вместо

 var = new ...() {};
0
30.05.2011 10:28:57

Это то, что вы будете делать в 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
23.01.2019 06:21:41

Я делаю это в 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
10.04.2019 13:36:56