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

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

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

new OptionParser () {

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

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

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

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
42
0
21 960
11
Перейти к ответу Данный вопрос помечен как решенный

Ответы 11

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

def printStuff():
   print "hello"

def doit(what):
   what()

doit(printStuff) 

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

-1 Я хочу создать подкласс optparse.OptionParser (), чтобы не передавать ссылки на методы.

Andrea Francia 11.12.2008 02:47

В таком случае в чем проблема с написанием настоящего подкласса? Я просто иллюстрировал наиболее распространенное использование анонимных внутренних классов.

Joachim Sauer 11.12.2008 02:53

+1: анонимные внутренние классы - это способ для Java облегчить сложный синтаксис. Python уже имеет простой синтаксис и не требует анонимных классов для облегчения нагрузки.

S.Lott 11.12.2008 15:32

Я знаю, что Python «лучше», но ответ заключается в передаче ссылок на методы, в то время как вопрос касается создания подклассов и переопределения одного метода.

Andrea Francia 11.12.2008 19:18

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

Joachim Sauer 12.12.2008 15:02

@saua: Верно. Я больше не могу голосовать за, stackoverflow говорит, что «Это голосование слишком старое, чтобы его можно было отменить или изменить».

Andrea Francia 15.12.2008 16:41

В 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

Правильный трек, но это не работает. Файл "t", строка 12, в <module> b.meth_a () TypeError: meth_b () принимает ровно 1 аргумент (задано 0)

gnud 11.12.2008 02:52

Справа: чтобы изменить метод объекта, вы должны изменить его на его метакласс. Я обновил код.

rob 11.12.2008 03:18

b .__ class __. meth_a = meth_b Влияет ли этот код на все объекты, основанные на классе «a» или только на объекте «b»?

Andrea Francia 11.12.2008 12:13

Лучше задать это как отдельный вопрос, чтобы ответы получили голоса.

James Hopkin 11.12.2008 12:49

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

rob 11.12.2008 21:18

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

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

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

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

gnud 11.12.2008 03:11

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

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

Если не считать создания именованного внутреннего класса и его экземпляра (что может быть предпочтительнее для удобства чтения), вариант 3 кажется хорошим способом сделать это.

Greg Case 11.12.2008 03:14

да - вот почему я написал «Что ты, наверное, хочешь». Я просто поставил это в последнюю очередь, потому что так текст сообщения выглядит лучше - и я хотел перечислить варианты.

gnud 11.12.2008 03:19

почему ты пользуешься новым? он устарел, так как довольно давно

rob 11.12.2008 03:45

поскольку мы перезаписываем ранее определенный метод, а новый метод с временным именем (some_method_upper) никогда не используется, почему бы не использовать лямбда-выражение и сделать его встроенным, поскольку он будет более согласован с анонимными встроенными классами.

fuentesjr 11.12.2008 05:28

не уверен, но может быть что-то вроде: obj.some_method = types.MethodType (lambda self: print self.val.upper (), obj)

fuentesjr 11.12.2008 05:31

Конечно - в этом тривиальном примере это можно сделать. Этот пример просто демонстрирует, как заменить один метод другим - в реальном примере лямбда может быть неоптимальной.

gnud 11.12.2008 14:24

@fuentesjr: вы не можете использовать print в лямбдах, если версия Python <2.6

jfs 11.12.2008 18:15

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

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

(править: исправлены опечатки в коде)

Спасибо, но я не понимаю строку custom_op = custom_op_class (). Следует ли его удалить?

Andrea Francia 11.12.2008 03:12

make_custom_op () возвращает класс. Вы должны вызвать класс, чтобы создать экземпляр, что и делает эта строка. Однако в моем коде в последних двух строках было несколько ошибок, которые я исправил на тот случай, если они вас запутали.

John Fouhy 11.12.2008 03:59

Это простой, но мощный метод, который при правильном использовании (а не только в качестве взлома) может привести к очень элегантному коду.

Daniel Naab 11.12.2008 08:43

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

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

p = MyOptionParser()

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

Ответ принят как подходящий

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

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

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

Это способ правильный для встраивания определений классов / создания анонимных классов (цитата из процитированной выше страницы руководства: «По сути, это динамическая форма оператора класса»); "принятый ответ" должен указывать на это.

Vlad 13.03.2012 04:47

Я не понимаю этого {"foo": lambda self: "foo"}. Он должен сам возвращаться?

zakdances 07.03.2013 20:10

Щелкните ссылку на документы. «словарь dict - это пространство имен, содержащее определения для тела класса, и становится атрибутом диктовать». Итак, «foo» - это имя метода, который принимает в качестве параметра только «self». Этот метод возвращает строку «foo».

Joe Hildebrand 08.03.2013 00:51

Это пример динамического класса, а не анонимного.

GuSuku 02.10.2019 23:35

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

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

вместо

 var = new ...() {};

Это то, что вы бы сделали в 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()

Я делаю это в 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, поэтому двойное подчеркивание означало бы «больше не использовать его» и могло бы измениться на без подчеркивания, если я начну использовать его где-нибудь еще.

Будучи извращенцем, вы можете использовать одноразовое имя _ для имени производного класса:

class _(OptionParser):

    def exit(self):
        pass  # your override impl

Другие вопросы по теме