Python 有匿名类吗?

我想知道 Python 是否有类似 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

具体的要求是能够在表达式上下文中创建一个具有指定字段的对象(例如,在 lambdas 和其他不允许使用语句的地方可用) ,不需要额外的外部声明,并且能够通过正常的成员访问语法 foo.bar按名称访问单个组件。创建的对象还应该实现结构化比较 通过组件名称(而不是像元组那样按位置进行)。

特别是: 元组不是因为它们的组件没有命名; 类不是因为它们需要声明; dicts 不是因为它们有不需要的 foo["bar"]语法来访问组件。

Namedtuple 不是这样的,因为即使您内联定义了类型,它仍然需要一个名称,并且比较是基于位置的,而不是基于名称的。特别是:

 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 标准库或任何流行的第三方库中是否有类似的东西。

[编辑]

这里有一个单表达式解决方案,它结合了 Ken 和 alanlcode 提供的两个信息量非常大的答案,在没有任何额外的外部声明的情况下,产生了结构相等性:

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

从技术上讲,它满足了问题的所有要求,但我真诚地希望没有人使用它(我肯定不会)。

65779 次浏览

The pythonic way would be to use a dict:

>>> foo = dict(x=1, y=2)
>>> bar = dict(y=2, x=1)
>>> foo == bar
True

Meets all your requirements except that you still have to do foo['x'] instead of foo.x.

If that's a problem, you could easily define a class such as:

class Bunch(object):
def __init__(self, **kwds):
self.__dict__.update(kwds)


def __eq__(self, other):
return self.__dict__ == other.__dict__

Or, a nice and short one

class Bunch(dict):
__getattr__, __setattr__ = dict.get, dict.__setitem__

(but note that this second one has problems as Alex points out in his comment!)

I don't remember offhand if there's a built-in but writing it yourself is shorter than typing your question. :-)

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

Quoted from this page:

 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}

1) See http://uszla.me.uk/space/blog/2008/11/06. You can create an anonymous object with slightly ugly syntax by using the type built-in function:

 anon_object_2 = type("", (), {})()

where the 3rd parameter is the dict that will contain the fields of your object.

 foo = type("", (), dict(y=1))()
foo.y == 1

2) Another variation is proposed by Peter Norvig at http://norvig.com/python-iaq.html. It is also similar to the answer posted by Ken.

class Struct:
def __init__(self, **entries): self.__dict__.update(entries)


>>> options = Struct(answer=42, linelen = 80, font='courier')
>>> options.answer
42

The benefit of this method is that you can implement equality by contents of the dict, which the first option doesn't have.

The type(...) form will fail the structural comparison requirement (without getting really ugly). The dict(...) form doesn't meet the attribute accessor requirement.

The attrdict seems to fall in the middle somewhere:

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

But it means defining a special class.

OK, I just noticed the update to the question. I'll just note that you can specify dict for the bases parameter and only need to specify the constructor then (in the icky type expression). I prefer attrdict. :-)

Looks like Python 3.3 has added exactly this thing in the form of types.SimpleNamespace class.

If you want the instance to be anonymous as well (using the object directly in an expression), then you're bound to use the type-expression. However in many cases the instance will not be anonymous, but assigned to a variable. This case can be handled in a reasonable way in python by using metaclasses or decorators.

An example using decorator:

def anonymous(cls):
return cls()


@anonymous
class foo:
x = 42


def bar(self):
return self.x

The decorator in this case causes the class foo to be instantiated an put into the variable foo instead of the class itself. The class itself will not be accessible from any namespace although it has a name:

>>> foo
<__main__.foo instance at 0x7fd2938d8320>
>>> foo.bar()
42

Another feature in python that accomodates for many use cases is that it's legal to define classes locally, which means that they would become a symbol local to that function, which in turns gives it some degree of anonymity.

enter image description here

As show above, I get a good intellisense from pycharm for this and I love this solution...

>>> def anonymous(cls):
...     return cls()
>>> class fooo:
...     @anonymous
...     class c:
...             D=10
>>> fooo.c.D
10