是否可以在 Python 中创建匿名对象?
Posted
技术标签:
【中文标题】是否可以在 Python 中创建匿名对象?【英文标题】:Is it possible to create anonymous objects in Python? 【发布时间】:2010-10-13 17:30:15 【问题描述】:我正在调试一些 Python,它以对象列表作为输入,每个对象都有一些属性。
我想硬编码一些测试值——比如说,一个包含四个对象的列表,它们的“foo”属性设置为某个数字。
还有比这更简洁的方法吗?
x1.foo = 1
x2.foo = 2
x3.foo = 3
x4.foo = 4
myfunc([x1, x2, x3, x4])
理想情况下,我只想说:
myfunc([<foo=1>, <foo=2>, <foo=3>, <foo=4>])
(显然,这是虚构的语法。但有没有类似的东西真的有效?)
注意:这永远不会被签入。这只是一些一次性的调试代码。所以不用担心可读性或可维护性。
【问题讨论】:
【参考方案1】:我发现了这个:http://www.hydrogen18.com/blog/python-anonymous-objects.html,在我有限的测试中,它似乎有效:
>>> obj = type('',(object,),"foo": 1)()
>>> obj.foo
1
【讨论】:
太棒了!而且是真正的单线! 爱它。这也适用于其他类和初始化,例如,这是一个从 Python 字典派生并使用两个条目初始化的匿名对象:type("", (dict,), )("a": 1, "b": 2)
【参考方案2】:
我喜欢 Tetha 的解决方案,但它过于复杂。
这里有一些更简单的东西:
>>> class MicroMock(object):
... def __init__(self, **kwargs):
... self.__dict__.update(kwargs)
...
>>> def print_foo(x):
... print x.foo
...
>>> print_foo(MicroMock(foo=3))
3
【讨论】:
但是,如果您要创建一大堆这些,则可以通过使用给定字典而不是创建新字典并复制所有条目来节省一些时间/内存。不过,这真的并不重要。 好吧,我可能是主观的,但我认为没有实质性的简化。是的,你使用 init 并更新,但你仍在摆弄 dict。 你是对的,没有明显的简化。它只是更短并且不使用 new,仅此而已。 如果您想要方便的语法,_
在 python 中是一个有效的类名,并且实际上并不比问题中提出的版本更难以理解。
使用最近的 Python 功能,您可以使用 "def mock(**kwargs): return type('',(),kwargs)()",并执行 "a = mock(foo=1 )"【参考方案3】:
如此简短,如此 Python!哎哟
>>> Object = lambda **kwargs: type("Object", (), kwargs)
那么你可以使用Object
作为一个通用的对象构造函数:
>>> person = Object(name = "Bernhard", gender = "male", age = 42)
>>> person.name
'Bernhard'
>>>
编辑:好吧,从技术上讲,这会创建一个类对象,而不是对象对象。但是您可以将其视为匿名对象,或者通过附加一对括号来修改第一行以立即创建实例:
>>> Object = lambda **kwargs: type("Object", (), kwargs)()
【讨论】:
这是我在这里看到的唯一“不可怕”的解决方案。 不错。应该是添加新关键字的 PEP。也许将名称从Object
更改为 Anon
?【参考方案4】:
看看这个:
class MiniMock(object):
def __new__(cls, **attrs):
result = object.__new__(cls)
result.__dict__ = attrs
return result
def print_foo(x):
print x.foo
print_foo(MiniMock(foo=3))
【讨论】:
【参考方案5】:也许你可以使用namedtuple 来解决这个问题:
from collections import namedtuple
Mock = namedtuple('Mock', ['foo'])
mock = Mock(foo=1)
mock.foo // 1
【讨论】:
【参考方案6】:另一个明显的 hack:
class foo1: x=3; y='y'
class foo2: y=5; x=6
print(foo1.x, foo2.y)
但是对于您的确切用例,直接使用匿名对象调用函数,我不知道有任何单行比更简洁
myfunc(type('', (object,), 'foo': 3,), type('', (object,), 'foo': 4))
丑,能干,但不是真的。
【讨论】:
【参考方案7】:从 Python 3.3 开始,types.SimpleNamespace 可以满足您的需求:
myfunc([types.SimpleNamespace(foo=1), types.SimpleNamespace(foo=2), types.SimpleNamespace(foo=3), types.SimpleNamespace(foo=4)])
这有点罗嗦,但你可以用别名来清理它:
_ = types.SimpleNamespace
myfunc([_(foo=1), _(foo=2), _(foo=3), _(foo=4)])
现在这实际上非常接近您问题中的虚构语法。
【讨论】:
感谢@RoadieRich 在之前的评论中建议_
作为班级名称。
如果使用 _ 让你感到困扰(对我来说感觉有点恶心和 unpythonic),并且你真的不想输入这么多,你也可以只 from types import SimpleNamespace as Nsp
并使用 Nsp like _ in你的答案。
@JJC 当然!我目前工作的编码标准不允许像这样在导入时重命名,所以我倾向于忘记这甚至是一个选项。【参考方案8】:
anonymous_object = type('',(),'name':'woody', 'age':'25')()
anonymous_object.name
> 'woody'
有一种很酷但很难理解的方法。 它使用 type() 创建一个具有默认初始化参数的无名类, 然后在没有任何参数的情况下初始化它并获取匿名对象。
【讨论】:
Python 应该支持匿名类型。它们在 C# 中非常方便。这是所有答案中最干净的解决方案。 @michael 是的,它是 C# 很酷的功能之一。 C# 有一个非常好的语法设计。【参考方案9】:我将使用 lambda
obj = lambda: None
obj.s = 'abb'
obj.i = 122
【讨论】:
和obj =
有什么区别?【参考方案10】:
非经典:
def mock(**attrs):
r = lambda:0
r.__dict__ = attrs
return r
def test(a, b, c, d):
print a.foo, b.foo, c.foo, d.foo
test(*[mock(foo=i) for i in xrange(1,5)])
# or
test(mock(foo=1), mock(foo=2), mock(foo=3), mock(foo=4))
【讨论】:
【参考方案11】:我就是这样做的:
from mock import patch
import requests
class MockResponse:
def __init__(self, text, status_code):
self.text = text
self.status_code = status_code
class TestSomething(unittest.TestCase):
@patch('requests.get',return_value=MockResponse('the result',200))
def test_some_request(self, *args, **kwargs):
response = requests.get('some.url.com')
assert response.text=='the result'
assert response.status_code=='200'
【讨论】:
【参考方案12】:如果您使用的是 Python 3.7 或更高版本,您可以使用named tuples 来增强创建的对象的不可变性、文档字符串和方便的元组方法:
from collections import namedtuple
PyObj = lambda **kwargs: namedtuple('PyObj', kwargs.keys())._make(kwargs.values())
o = PyObj(foo = 1)
print(o)
# prints: PyObj(foo=1)
o.foo
# returns: 1
o.foo = 0
# exception:
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# AttributeError: can't set attribute
print(PyObj(foo = 1, bar = 'baz'))
# prints: PyObj(foo=1, bar='baz')
需要 Python 3.7+ 以确保键和值的顺序相同。
但是,如果属性列表是预定义的,你可以直接使用namedtuple
,正如Huachao建议的那样,不需要定义PyObj
函数,你可以在v2.7中使用它。
from collections import namedtuple
foo = namedtuple('Foo', 'foo')
myfunc = lambda l: [x.foo * 10 for x in l]
myfunc([foo(1), foo(2), foo(3), foo(4)])
# returns [10, 20, 30, 40]
另外,它看起来更像您正在寻找的语法。
【讨论】:
【参考方案13】:是的,我非常怀念 javascript 中直截了当的匿名对象,尤其是在函数返回值中,你可以直接说
function george()
return fred:6, jim:9;
x = george();
y = x.fred;
您可以使用字典来获得相同的效果,但是所有这些方括号和单引号看起来都很混乱。所以我现在执行以下操作:
def fred():
class rv:
x=0
rv.y = 6
return rv
def jim():
class rv:
x=0
rv.y = 9
return rv
a = fred()
b = jim()
print(a.y, b.y, id(a.y), id(b.y))
如果有一个全局类 RV 会感觉更好,并实例化它以获得相同的效果,但是这样函数就没有外部依赖了。
【讨论】:
以上是关于是否可以在 Python 中创建匿名对象?的主要内容,如果未能解决你的问题,请参考以下文章