Python有匿名类吗?

Pav*_*aev 66 python

我想知道Python是否有类似C#匿名类的功能.为了澄清,这是一个示例C#片段:

var foo = new { x = 1, y = 2 };
var bar = new { y = 2, x = 1 };
foo.Equals(bar); // "true"
Run Code Online (Sandbox Code Playgroud)

在Python中,我会想象这样的事情:

foo = record(x = 1, y = 2)
bar = record(y = 2, x = 1)
foo == bar  # true
Run Code Online (Sandbox Code Playgroud)

具体要求是能够在表达式上下文中创建具有指定字段的对象(例如,在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
Run Code Online (Sandbox Code Playgroud)

我知道如果需要,如何在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)
Run Code Online (Sandbox Code Playgroud)

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

dF.*_*dF. 48

pythonic的方式是使用dict:

>>> foo = dict(x=1, y=2)
>>> bar = dict(y=2, x=1)
>>> foo == bar
True
Run Code Online (Sandbox Code Playgroud)

满足您的所有要求,除了您仍然需要做foo['x']而不是foo.x.

如果这是一个问题,您可以轻松定义一个类,如:

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

    def __eq__(self, other):
        return self.__dict__ == other.__dict__
Run Code Online (Sandbox Code Playgroud)

或者,一个漂亮而短暂的

class Bunch(dict):
    __getattr__, __setattr__ = dict.get, dict.__setitem__
Run Code Online (Sandbox Code Playgroud)

(但请注意,第二个问题正如Alex在评论中指出的那样!)

  • 我认为"束"(第一种形式)是正确的答案(但是自从8年前我创造了这个名字后我就有了偏见 - 请参阅http://code.activestate.com/recipes/52308/其中btw是顶级谷歌搜索搜索[束python] ;-).第二种形式有深层次和有些微妙的问题,做x =束(更新= 23)并看看x.update*IS*;-) - 你称之为NICE? - ) (11认同)
  • 从问题 - "......这不是因为他们有不受欢迎的foo ["bar"]语法来访问组件." (6认同)
  • 尽管如此,请求就是这样,但这并没有回答它.考虑到`namedtuple`的存在,我也没有看到它是如何"非Pythonic",它提供了我想要的大部分语法糖. (3认同)
  • @Alex:如何设置`__getattribute__ = dict.get`?丑陋,是的,但它仍有问题吗? (2认同)

ala*_*ode 39

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

 anon_object_2 = type("", (), {})()
Run Code Online (Sandbox Code Playgroud)

其中第3个参数是包含对象字段的dict.

 foo = type("", (), dict(y=1))()
 foo.y == 1
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

这种方法的好处是你可以通过dict的内容实现相等,第一个选项没有.


Pav*_*aev 36

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

  • 这个答案应该在顶部. (2认同)
  • 是否有与“SimpleNamespace”一起使用的匿名类型提示?假设我正在定义一个返回 SimpleNamespace(x= ..., y= ...) 的函数,我希望能够以如下方式键入提示结果: `def func() -> SimpleNamespace(x : int, y: str)`(不过语法必须改变)。这类似于在元组作为返回类型的情况下可以执行的操作(即“def func() -> Tuple[int, str]”)。 (2认同)

Ken*_*Ken 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
Run Code Online (Sandbox Code Playgroud)

  • `__neq__`应为`__ne__`,不会自动提供. (4认同)
  • 顺便说一下,真的需要`__neq__`吗?是不是默认定义为"不__eq__"自动提供? (2认同)

ars*_*ars 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
Run Code Online (Sandbox Code Playgroud)

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

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


sky*_*ing 5

如果您也希望实例是匿名的(直接在表达式中使用对象),那么您必须使用类型表达式。然而,在许多情况下,实例不是匿名的,而是分配给变量。这种情况可以在 python 中通过使用元类或装饰器以合理的方式处理。

使用装饰器的示例:

def anonymous(cls):
    return cls()

@anonymous
class foo:
     x = 42

     def bar(self):
          return self.x
Run Code Online (Sandbox Code Playgroud)

在这种情况下,装饰器会导致类foo被实例化并放入变量foo而不是类本身。尽管该类有一个名称,但它本身无法从任何命名空间访问:

>>> foo
<__main__.foo instance at 0x7fd2938d8320>
>>> foo.bar()
42
Run Code Online (Sandbox Code Playgroud)

Python 中适用于许多用例的另一个功能是在本地定义类是合法的,这意味着它们将成为该函数的本地符号,这反过来又赋予它一定程度的匿名性。