Есть ли в Python анонимные классы? - PullRequest
64 голосов
/ 14 июля 2009

Мне интересно, есть ли в 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]

Просто ради этого, вот решение с одним выражением, которое объединяет два очень информативных ответа Кена и alanlcode, обеспечивая структурное равенство без каких-либо дополнительных внешних деклараций:

type("", (), { \
    "__init__": (lambda self, **kwargs: self.__dict__.update(kwargs)), \
    "__eq__": (lambda self, other: self.__dict__ == other.__dict__) } \
)(x = 1, y = 2)

Технически, он удовлетворяет всем требованиям вопроса, но я искренне надеюсь, что никто никогда не использует его (я определенно не буду).

Ответы [ 8 ]

47 голосов
/ 14 июля 2009

Питонический способ будет использовать 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__

(но обратите внимание, что у этого второго есть проблемы, как Алекс указывает в своем комментарии!)

38 голосов
/ 14 июля 2009

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, которого нет в первом варианте.

35 голосов
/ 12 декабря 2012

Похоже, что Python 3.3 добавил именно эту вещь в виде types.SimpleNamespace класса.

6 голосов
/ 14 июля 2009

Тип (...) форма не выполнит требования структурного сравнения (без уродства). Форма dict (...) не соответствует требованию доступа к атрибуту.

Атрибут кажется где-то посередине:

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 голосов
/ 14 июля 2009

Я не помню, если есть встроенное, но написать его самому короче, чем набрать свой вопрос. : -)

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
4 голосов
/ 30 мая 2015

Если вы хотите, чтобы экземпляр также был анонимным (используя объект непосредственно в выражении), вы обязаны использовать выражение типа. Однако во многих случаях экземпляр не будет анонимным, а будет назначен переменной. Этот случай может быть разумно обработан в 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, которая подходит для многих случаев использования, состоит в том, что допустимо локально определять классы, что означает, что они станут символом, локальным для этой функции, что, в свою очередь, дает ей некоторую степень анонимности.

4 голосов
/ 14 июля 2009

Цитируется с этой страницы :

 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}
0 голосов
/ 02 августа 2018

enter image description here

Как показано выше, я получаю хороший intellisense от pycharm за это, и мне нравится это решение ...

>>> def anonymous(cls):
...     return cls()
>>> class fooo:
...     @anonymous
...     class c:
...             D=10
>>> fooo.c.D
10
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...