Python是否有匿名类?


78

我想知道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。创建的对象还应按组件名称 (而不是像元组那样按位置)实现结构比较。

特别是:元组不是因为它们的组件没有命名;类不是因为它们需要声明;dict是不是因为它们具有不希望使用的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)

从技术上讲,它可以满足问题的所有要求,但我衷心希望没有人会使用它(我绝对不会)。


2
听起来像字典应该做的。我发现最好找到python的方法,而不是将另一种语言适合python。顺便说一句-如果您不喜欢字典,可以使用foo [“ bar”]访问方法,或者使用get方法:foo.get(“ bar”)
monkut

1
鉴于可以在Python中轻松实现这一点,因此我看不出没有这样做的特殊原因,并且绝对不要认为它“使另一种语言适合Python”。特别是因为它似乎非常接近现有namedtuple的意图。
帕维尔米纳夫

2
我发现问X语言是否具有Y语言的特征,然后要求所有内容完全相同是很奇怪的。语言并不完全相同。Python没有匿名函数,但是它们有字典,它们也可以正常工作。是的,访问语法不同。大笔交易。
Lennart Regebro

3
我不是在要求功能完全相同-如果是的话,我也会在要求静态类型和不变性;)我只是在要求语法,而我认为这样更自然和方便。
帕维尔米纳夫

我倾向于创建对象,因此可以在一开始就将数据粘贴在它们上以进行属性访问。您很快就会习惯听写。
Lennart Regebro

Answers:


50

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在评论中指出的那样!)


6
从问题开始-“ ... dict不是,因为它们具有不需要的foo [“ bar”]语法来访问组件。”
太多的PHP

这有效,除了访问东西是foo ['x']而不是foo.x
Mike Cooper

3
尽管如此,请求就是它的原样,但这并不能解决问题。考虑到的存在,我也看不到它是“非Python语言的” namedtuple,它提供了我想要的大多数语法糖。
帕维尔·米纳夫09年

12
我认为“ Bunch”(第一种形式)是正确的答案(但自8年前我创造这个名称以来,我一直有偏见-请参阅code.activestate.com/recipes/52308,顺便说一句,这是Google最受欢迎的搜索引擎搜索[bunch python] ;-)。第二种形式存在深层且有些微妙的问题,请执行x = Bunch(update = 23)并查看x.update IS ; -)
Alex Martelli

2
@Alex:设置如何__getattribute__ = dict.get?丑陋的,是的,但是仍然有问题吗?
dF。

58

看起来Python 3.3已经以types.SimpleNamespace类的形式添加了这个东西。


6
这个答案应该在顶部。
balu

python有很多隐藏的部分,它们确实应该更加突出并嵌入在这种语言中。似乎不愿真正地对语言本身进行现代化,而不是添加这些附加功能。答案对我有用,每当我想这样做时,我只需要继续回到这个问题上!
Mark Adamson

是否有匿名的类型提示SimpleNamespace?假设我正在定义一个返回SimpleNamespace(x = ...,y = ...)的函数,我希望能够以如下方式键入结果的提示:(def func() -> SimpleNamespace(x: int, y: str)尽管语法必须更改) 。这类似于在元组作为返回类型(即def func() -> Tuple[int, str])的情况下可以执行的操作。
amka66

44

1)参见http://uszla.me.uk/space/blog/2008/11/06。您可以使用type内置函数创建一个语法稍差的匿名对象:

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

其中第三个参数是将包含对象字段的字典。

 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的内容实现相等性,而这是第一个选项所没有的。


我喜欢“结构”课。对我来说非常有用,因为我的问题不需要任何特殊的方法或运算符。简而言之,我正在使用肥皂水/肥皂,并且大多数时候肥皂水会为我建立一个“回复”对象,该对象由WSDL定义。如果得到的XML不好,sax解析器将引发异常,使您没有回复对象。我用上面的Struct类“伪造”了一个应答对象(仅在我的应用程序中设置属性“ error”和“ message”)并将其传递给下游。如果错误处理期望更多的属性(或方法?),则很容易添加(扩展?)此类以进行匹配。赢得。
FlipMcF 2012年

6

type(...)形式将无法通过结构比较的要求(而不会变得非常难看)。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

但这意味着定义一个特殊的类。

好的,我刚刚注意到问题的更新。我只是注意到您可以dict为bases参数指定,然后只需指定构造函数(在icky类型表达式中)。我更喜欢attrdict。:-)


1
设置self .__ dict__ = self会导致内存泄漏,因此我建议您不要这样做。bugs.python.org/issue1469629
rmmh 2010年

__init__通话看起来不是这样super(attrdict, self).__init__(*args, **kwargs)吗?
kasperd 2015年

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

1
整洁(我通常知道如何执行此操作,但没有意识到它是如此简单)。现在,提出一个真正的问题:您愿意为上述内容提交PEP吗?:)
Pavel Minaev'2009年

2
顺便说一句,__neq__真的需要吗?默认定义不是not __eq__自动提供的吗?
帕维尔·米纳夫

1
帕维尔:这就是我最初的想法,但是当我尝试它时,它似乎并没有那样工作(尽管我很有可能搞砸了)。

4
__neq__应该为__ne__,并且不会自动提供。
伊桑·弗曼

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中另一个可以满足许多用例的功能是,在本地定义类是合法的,这意味着它们将成为该函数本地的符号,从而使它具有某种程度的匿名性。


4

引用此页

 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}

它不是NEQ它的NE
让·弗朗索瓦·法布尔

-1

在此处输入图片说明

如上所示,我从pycharm获得了很好的智能感知,我喜欢这种解决方案...

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

@skyking使用您的提示,我喜欢这个...我对stackoverflow有更多规定
TooGeeky
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.