Есть ли у Python анонимные классы?
мне интересно, есть ли у Python что-нибудь вроде функции анонимных классов C#. Чтобы уточнить, вот пример фрагмента C#:
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
конкретное требование заключается в возможности создавать объект с указанными полями в контексте выражения (например, используемый в лямбда-выражениях и других местах, где операторы не разрешены), без дополнительных внешних объявлений и возможности доступа к отдельным компонентам С помощью имя через обычный синтаксис доступа к членам foo.bar
. Созданный объект также должен реализовывать структурное сравнение по именам компонент (не по позиции, как это делают кортежи).
в частности: кортежи не потому, что их компоненты не названы; классы не потому, что они требуют объявления; дикты не потому, что они нежелательны 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]
просто ради этого, вот решение с одним выражением, которое сочетает в себе два очень информативных ответа от Ken и alanlcode, давая структурное равенство без каких-либо дополнительных внешних деклараций:
type("", (), {
"__init__": (lambda self, **kwargs: self.__dict__.update(kwargs)),
"__eq__": (lambda self, other: self.__dict__ == other.__dict__) }
)(x = 1, y = 2)
технически, он удовлетворяет всем требованиям вопроса, но я искренне надеюсь, что никто не использует его (я наверняка не буду).
8 ответов:
в подходящие для Python способ, чтобы использовать
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__
(но обратите внимание, что у этого второго есть проблемы, как указывает Алекс в своем комментарии!)
1) см.http://uszla.me.uk/space/blog/2008/11/06. Вы можете создать анонимный объект со слегка уродливым синтаксисом, используя
type
встроенные функции:anon_object_2 = type("", (), {})()
где 3-й параметр-это словарь, который будет содержать поля вашего объекта.
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, которого нет в первом варианте.
похоже, Python 3.3 добавил Именно эту вещь в виде
types.SimpleNamespace
класса.
тип(...) форма не будет соответствовать требованиям структурного сравнения (не становясь действительно уродливой). Словарь.(..) форма не соответствует требованию доступа к атрибутам.
The 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
для параметра bases и только нужно указать затем конструктор (в выражении типа icky). Я предпочитаю attrdict. : -)
Я не помню навскидку, если есть встроенный, но писать его самостоятельно короче, чем вводить свой вопрос. : -)
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
если вы хотите, чтобы экземпляр был анонимным (используя объект непосредственно в выражении), то вы обязаны использовать тип-выражение. Однако во многих случаях экземпляр не будет анонимным, а будет присвоен переменной. Этот случай может быть обработан разумным способом в 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, которая подходит для многих случаев использования, заключается в том, что законно определять классы локально, что означает, что они станут символом, локальным для этой функции, что, в свою очередь, дает ей некоторую степень анонимности.
цитата из на этой странице:
class Struct: def __init__(self, **entries): self.__dict__.update(entries) def __eq__(self, other): return self.__dict__ == other.__dict__ def __ne__(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}