Python有匿名类吗?

95 投票
9 回答
76288 浏览
提问于 2025-04-15 12:53

我在想,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

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

特别地:元组不行,因为它们的组件没有名称;类也不行,因为它们需要声明;字典也不行,因为它们的组件访问方式是 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)

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

9 个回答

54

1) 你可以查看这个链接:http://uszla.me.uk/space/blog/2008/11/06。通过使用 type 这个内置函数,你可以创建一个匿名对象,虽然语法看起来有点复杂:

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

这里的第三个参数是一个字典,它会包含你对象的字段。

 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

这种方法的好处是,你可以通过字典的内容来实现相等性,而第一个选项没有这个功能。

82

看起来Python 3.3版本新增了这个功能,具体是通过一个叫做 types.SimpleNamespace 的类来实现的。

56

用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__

(不过要注意,Alex在他的评论中提到这个简短的写法有问题!)

撰写回答