В Java вы можете определить новый встроенный класс, используя анонимные внутренние классы. Это полезно, когда вам нужно переписать только один метод класса.
Предположим, вы хотите создать подкласс OptionParser, который переопределяет только один метод (например, exit()). В Java можно написать примерно так:
new OptionParser () {
public void exit() {
// body of the method
}
};
Этот фрагмент кода создает анонимный класс, который расширяет OptionParser и переопределяет только метод exit().
В Python есть похожая идиома? Какая идиома используется в этих обстоятельствах?






Java использует анонимные классы в основном для имитации замыканий или просто блоков кода. Поскольку в Python вы можете легко передавать методы, нет необходимости в такой неуклюжей конструкции, как анонимные внутренние классы:
def printStuff():
print "hello"
def doit(what):
what()
doit(printStuff)
Обновлено: я знаю, что это не то, что нужно в этом особом случае. Я только что описал наиболее распространенное решение проблемы с помощью Python, которое чаще всего используется анонимными внутренними классами в Java.
В таком случае в чем проблема с написанием настоящего подкласса? Я просто иллюстрировал наиболее распространенное использование анонимных внутренних классов.
+1: анонимные внутренние классы - это способ для Java облегчить сложный синтаксис. Python уже имеет простой синтаксис и не требует анонимных классов для облегчения нагрузки.
Я знаю, что Python «лучше», но ответ заключается в передаче ссылок на методы, в то время как вопрос касается создания подклассов и переопределения одного метода.
Я бы не сказал, что Python лучше. У каждого языка есть своя сила. Передавать вызываемый код в Python проще, чем в Java. И я знаю, что вы ищете что-то еще, я сохраню свой ответ здесь на случай, если кто-то еще найдет ваш вопрос и будет искать то, что я предлагаю.
@saua: Верно. Я больше не могу голосовать за, stackoverflow говорит, что «Это голосование слишком старое, чтобы его можно было отменить или изменить».
В 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)
Справа: чтобы изменить метод объекта, вы должны изменить его на его метакласс. Я обновил код.
b .__ class __. meth_a = meth_b Влияет ли этот код на все объекты, основанные на классе «a» или только на объекте «b»?
Лучше задать это как отдельный вопрос, чтобы ответы получили голоса.
Я думаю, что на этот вопрос можно ответить намного лучше, просто поиграв в интерактивную оболочку. Это одно из основных преимуществ Python: есть сомнения или вопросы по языковой функции? хорошо, поиграйте с ним в sjhell и посмотрите, действительно ли это.
У Python, вероятно, есть лучшие способы решить вашу проблему. Если бы вы могли предоставить более конкретную информацию о том, что вы хотите сделать, это поможет.
Например, если вам нужно изменить метод, вызываемый в определенной точке кода, вы можете сделать это, передав функцию в качестве параметра (функции являются объектами первого класса в python, вы можете передавать их функциям и т. д.). Вы также можете создавать анонимные функции lambda (но они ограничены одним выражением).
Кроме того, поскольку python очень динамичен, вы можете изменять методы объекта после его создания object.method1 = alternative_impl1, хотя на самом деле это немного сложнее, см. ответ gnud
На самом деле вы не можете заменить методы таким образом - см. Мой ответ, чтобы узнать правильный способ.
Вы можете сделать это тремя способами:
Пример варианта 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 кажется хорошим способом сделать это.
да - вот почему я написал «Что ты, наверное, хочешь». Я просто поставил это в последнюю очередь, потому что так текст сообщения выглядит лучше - и я хотел перечислить варианты.
почему ты пользуешься новым? он устарел, так как довольно давно
поскольку мы перезаписываем ранее определенный метод, а новый метод с временным именем (some_method_upper) никогда не используется, почему бы не использовать лямбда-выражение и сделать его встроенным, поскольку он будет более согласован с анонимными встроенными классами.
не уверен, но может быть что-то вроде: obj.some_method = types.MethodType (lambda self: print self.val.upper (), obj)
Конечно - в этом тривиальном примере это можно сделать. Этот пример просто демонстрирует, как заменить один метод другим - в реальном примере лямбда может быть неоптимальной.
@fuentesjr: вы не можете использовать print в лямбдах, если версия Python <2.6
Ну, классы - это объекты первого класса, поэтому вы можете создавать их в методах, если хотите. например
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 (). Следует ли его удалить?
make_custom_op () возвращает класс. Вы должны вызвать класс, чтобы создать экземпляр, что и делает эта строка. Однако в моем коде в последних двух строках было несколько ошибок, которые я исправил на тот случай, если они вас запутали.
Это простой, но мощный метод, который при правильном использовании (а не только в качестве взлома) может привести к очень элегантному коду.
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 в список базовых классов.
Это способ правильный для встраивания определений классов / создания анонимных классов (цитата из процитированной выше страницы руководства: «По сути, это динамическая форма оператора класса»); "принятый ответ" должен указывать на это.
Я не понимаю этого {"foo": lambda self: "foo"}. Он должен сам возвращаться?
Щелкните ссылку на документы. «словарь dict - это пространство имен, содержащее определения для тела класса, и становится атрибутом диктовать». Итак, «foo» - это имя метода, который принимает в качестве параметра только «self». Этот метод возвращает строку «foo».
Это пример динамического класса, а не анонимного.
Вы всегда можете скрыть класс по переменным:
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
-1 Я хочу создать подкласс optparse.OptionParser (), чтобы не передавать ссылки на методы.