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
具体的需求是能够在表达式上下文中创建一个带有指定字段的对象(比如在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 个回答
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
这种方法的好处是,你可以通过字典的内容来实现相等性,而第一个选项没有这个功能。
看起来Python 3.3版本新增了这个功能,具体是通过一个叫做 types.SimpleNamespace
的类来实现的。
用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在他的评论中提到这个简短的写法有问题!)