我想知道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
。创建的对象还应该通过组件名称实现结构比较(不是按位置,如元组那样)。
特别是:元组不是因为它们的组件没有命名;课程不是因为它们需要声明; 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)
从技术上讲,它满足了问题的所有要求,但我真诚地希望没有人使用它(我绝对不会)。
答案 0 :(得分:47)
pythonic方式是使用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在评论中指出的那样!)
答案 1 :(得分:38)
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)Peter Norvig在http://norvig.com/python-iaq.html提出了另一种变体。它也与Ken发布的答案类似。
class Struct:
def __init__(self, **entries): self.__dict__.update(entries)
>>> options = Struct(answer=42, linelen = 80, font='courier')
>>> options.answer
42
这种方法的好处是你可以通过dict的内容实现相等,第一个选项没有。
答案 2 :(得分:35)
看起来Python 3.3已经以types.SimpleNamespace
类的形式添加了这个东西。
答案 3 :(得分:6)
我不记得是否有内置内容但是自己编写它比输入问题要短。 : - )
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 :(得分:6)
类型(...)形式将失败结构比较要求(没有变得非常丑陋)。 dict(...)表单不符合属性访问者要求。
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
但这意味着定义一个特殊的类。
好的,我刚刚注意到问题的更新。我只需要注意,您可以为bases参数指定dict
,然后只需要指定构造函数(在icky类型表达式中)。我更喜欢attrdict。 : - )
答案 5 :(得分:4)
引自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}
答案 6 :(得分:4)
如果您希望实例也是匿名的(直接在表达式中使用该对象),那么您必须使用type-expression。但是在许多情况下,实例不是匿名的,而是分配给变量。通过使用元类或装饰器,可以在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中适用于许多用例的另一个特性是在本地定义类是合法的,这意味着它们将成为该函数的本地符号,从而使其具有一定程度的匿名性。 / p>
答案 7 :(得分:0)