Есть ли у Python анонимные классы?
Мне интересно, есть ли у Python что-то вроде функции анонимных классов С#. Чтобы прояснить, здесь пример фрагмента С#:
var foo = new { x = 1, y = 2 };
var bar = new { y = 2, x = 1 };
foo.Equals(bar); // "true"
В Python я бы предположил что-то вроде этого:
foo = record(x = 1, y = 2)
bar = record(y = 2, x = 1)
foo == bar # true
Конкретным требованием является создание объекта с указанными полями в контексте выражения (например, использование в lambdas и других местах, где утверждения не допускаются), без дополнительных внешних объявлений и возможности доступа к отдельным компонентам по имени через синтаксис доступа к нормальному члену foo.bar
. Созданный объект также должен реализовывать структурное сравнение по именам компонентов (не по положению, как это делают кортежи).
В частности: кортежи - это не потому, что их компоненты не называются; классы - это не потому, что они требуют объявления; dicts не потому, что они имеют нежелательный синтаксис foo["bar"]
для доступа к компонентам.
namedtuple не так, потому что он все еще требует имени, даже если вы определяете тип inline, а сравнение - а не на основе имени. В частности:
def foo(): return namedtuple("Foo", "x y")(x = 1, y = 2)
def bar(): return namedtuple("Foo", "y x")(x = 1, y = 2)
foo() == bar() # False because fields are compared in order, and not by name
# True would be desired instead
Я знаю, как писать такую вещь в Python, если это необходимо. Но я хотел бы знать, есть ли что-нибудь подобное в стандартной библиотеке Python или в каких-либо популярных сторонних библиотеках.
[EDIT]
Просто ради этого, здесь одно выражение выражение, которое объединяет два очень информативных ответов Кена и alanlcode, что дает структурное равенство без каких-либо дополнительных внешних заявлений:
type("", (), { \
"__init__": (lambda self, **kwargs: self.__dict__.update(kwargs)), \
"__eq__": (lambda self, other: self.__dict__ == other.__dict__) } \
)(x = 1, y = 2)
Технически, он удовлетворяет всем требованиям вопроса, но я искренне надеюсь, что никто никогда его не использует (я определенно не буду).
Ответы
Ответ 1
Питоновским способом было бы использовать dict
:
>>> foo = dict(x=1, y=2)
>>> bar = dict(y=2, x=1)
>>> foo == bar
True
Отвечает всем вашим требованиям, за исключением того, что вам еще нужно сделать foo['x']
вместо foo.x
.
Если это проблема, вы можете легко определить класс, например:
class Bunch(object):
def __init__(self, **kwds):
self.__dict__.update(kwds)
def __eq__(self, other):
return self.__dict__ == other.__dict__
Или, хороший и короткий
class Bunch(dict):
__getattr__, __setattr__ = dict.get, dict.__setitem__
(но обратите внимание, что этот второй имеет проблемы, как указывает Алекс в своем комментарии!)
Ответ 2
1) См. http://uszla.me.uk/space/blog/2008/11/06. Вы можете создать анонимный объект со слегка уродливым синтаксисом с помощью встроенной функции type
:
anon_object_2 = type("", (), {})()
где третий параметр - это dict, который будет содержать поля вашего объекта.
foo = type("", (), dict(y=1))()
foo.y == 1
2) Еще один вариант предложен Петром Норвигом в http://norvig.com/python-iaq.html. Это также похоже на ответ, отправленный Кеном.
class Struct:
def __init__(self, **entries): self.__dict__.update(entries)
>>> options = Struct(answer=42, linelen = 80, font='courier')
>>> options.answer
42
Преимущество этого метода состоит в том, что вы можете реализовать равенство по содержимому dict, которого первый параметр не имеет.
Ответ 3
Похоже, что Python 3.3 добавил именно эту вещь в виде types.SimpleNamespace
class.
Ответ 4
Я не помню, если бы был встроен, но писать сам был короче, чем печатать свой вопрос.: -)
class record(object):
def __init__(self, **kwargs): self.__dict__ = kwargs
def __eq__(self, r2): return self.__dict__ == r2.__dict__
def __ne__(self, r2): return self.__dict__ != r2.__dict__
foo = record(x=1, y=2)
bar = record(y=2, x=1)
foo == bar # => true
Ответ 5
Форма типа (...) приведет к отказу от требования к структурному сопоставлению (без получения действительно уродливого). Форма dict (...) не соответствует требованию доступа к атрибуту.
attrdict, кажется, где-то посередине:
class attrdict(dict):
def __init__(self, *args, **kwargs):
dict.__init__(self, *args, **kwargs)
self.__dict__ = self
a = attrdict(x=1, y=2)
b = attrdict(y=2, x=1)
print a.x, a.y
print b.x, b.y
print a == b
Но это означает определение специального класса.
ОК, я только что заметил обновление к вопросу. Я просто заметлю, что вы можете указать dict
для параметра base, и только тогда нужно указать конструктор (в выражении типа icky). Я предпочитаю attrdict.: -)
Ответ 6
Цитата из эта страница:
class Struct:
def __init__(self, **entries): self.__dict__.update(entries)
def __eq__(self, other): return self.__dict__ == other.__dict__
def __neq__(self, other): return self.__dict__ != other.__dict__
options = Struct(answer=42, linelen = 80, font='courier')
options.answer
>>> 42
options.answer = 'plastics'
vars(options)
>>> {'answer': 'plastics', 'font': 'courier', 'linelen': 80}
Ответ 7
Если вы хотите, чтобы экземпляр был анонимным (используя объект непосредственно в выражении), вы должны использовать выражение типа. Однако во многих случаях экземпляр не будет анонимным, но назначается переменной. Этот случай можно обработать разумным способом в python, используя метаклассы или декораторы.
Пример использования декоратора:
def anonymous(cls):
return cls()
@anonymous
class foo:
x = 42
def bar(self):
return self.x
Декоратор в этом случае заставляет класс foo
быть помещен в переменную foo
вместо самого класса. Сам класс не будет доступен из любого пространства имен, хотя он имеет имя:
>>> foo
<__main__.foo instance at 0x7fd2938d8320>
>>> foo.bar()
42
Еще одна особенность в python, которая подходит для многих случаев использования, заключается в том, что легально определять классы локально, что означает, что они станут символом, локальным для этой функции, что по очереди дает ей некоторую степень анонимности.